├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── core ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── woelker │ │ └── jimix │ │ └── core │ │ ├── AssetRequestHandler.java │ │ ├── HttpRequest.java │ │ ├── InventoryRequestHandler.java │ │ ├── JimixIOUtils.java │ │ ├── JimixRequestHandler.java │ │ ├── JsonWriter.java │ │ ├── MbeanOperationRequestHandler.java │ │ ├── MbeanRequestHandler.java │ │ ├── RequestHandler.java │ │ ├── TypeCoercer.java │ │ └── logging │ │ ├── JuliLogger.java │ │ ├── Logger.java │ │ ├── LoggerFactory.java │ │ └── Slf4jLogger.java │ └── test │ └── java │ └── org │ └── woelker │ └── jimix │ └── core │ ├── JsonWriterTest.java │ └── TypeCoercerTest.java ├── dropwizard ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── woelker │ └── jimix │ └── dropwizard │ └── JimixBundle.java ├── how-to-release.txt ├── pom.xml ├── samples ├── dropwizard │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── woelker │ │ └── jimix │ │ └── samples │ │ └── dropwizard │ │ └── JimixSampleDropwizard.java ├── jetty │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── org │ │ │ └── woelker │ │ │ └── jimix │ │ │ └── samples │ │ │ └── jetty │ │ │ └── JimixSampleJetty.java │ │ └── resources │ │ └── logback.xml ├── pom.xml ├── vertx │ ├── pom.xml │ ├── pom.xml.old │ └── src │ │ └── main │ │ ├── assembly │ │ └── mod.xml │ │ ├── java │ │ └── org │ │ │ └── woelker │ │ │ └── jimix │ │ │ └── samples │ │ │ └── vertx │ │ │ └── JimixSampleVertxMain.java │ │ └── resources │ │ └── mod.json └── war │ ├── pom.xml │ └── src │ └── main │ └── webapp │ ├── WEB-INF │ └── web.xml │ └── index.jsp ├── servlet ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── woelker │ │ └── jimix │ │ └── servlet │ │ ├── JimixServlet.java │ │ └── ServletHttpRequestWrapper.java │ └── test │ └── java │ └── org │ └── woelker │ └── jimix │ └── servlet │ ├── JimixSandbox.java │ └── JimixServletIntegrationTest.java ├── ui ├── .editorconfig ├── .gitignore ├── assets-assembly.xml ├── gulpfile.js ├── package-lock.json ├── package.json ├── pom.xml ├── src │ ├── actions │ │ └── actions.js │ ├── application │ │ ├── Application.js │ │ ├── NavigationBar.js │ │ └── routes.js │ ├── components │ │ └── Sparkline.js │ ├── index.html │ ├── inventory │ │ └── InventoryStore.js │ ├── main.js │ ├── mbeans │ │ ├── MBeansView.js │ │ ├── MbeanStore.js │ │ ├── details │ │ │ ├── AttributeValue.js │ │ │ └── MbeanDetailsContainer.js │ │ └── list │ │ │ └── MbeanListContainer.js │ └── style │ │ └── style.css ├── webpack.config.js └── yarn.lock ├── vertx ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── woelker │ └── jimix │ └── vertx │ ├── JimixVertxHandler.java │ └── VertxHttpRequestWrapper.java └── war ├── pom.xml └── src └── main └── webapp └── WEB-INF └── web.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | nb-configuration.xml 4 | *.iml -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk7 4 | - oraclejdk7 5 | - oraclejdk8 6 | -------------------------------------------------------------------------------- /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, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jimix 2 | 3 | [![Build Status](https://travis-ci.org/manuel-woelker/jimix.svg)](https://travis-ci.org/manuel-woelker/jimix) [![Maven Central](https://img.shields.io/maven-central/v/org.woelker.jimix/jimix.svg)]() 4 | 5 | A modern JMX web console 6 | 7 | ## Features 8 | 9 | * Zero third-party dependencies 10 | * Basic Web UI 11 | * Simple REST interface (CORS enabled) 12 | * Auto-refresh with display of value deltas 13 | * Invoking operations and setting attributes 14 | 15 | ## Screenshot 16 | 17 | ![screenshot](https://raw.github.com/manuel-woelker/jimix/screenshots/screenshot.png) 18 | 19 | ## Changelog 20 | 21 | ##### 1.2.1 (2014-08-19) 22 | * (fix) core/ui: properly map NaN and infinity values to null in JSON 23 | 24 | ##### 1.2.0 (2014-05-11) 25 | * Initial public release 26 | 27 | ## Planned features 28 | 29 | * Adapters for all major web frameworks 30 | * Authentication 31 | 32 | ## Usage 33 | 34 | Refer to the samples directory for detailed usage examples. 35 | 36 | ### Standalone WAR 37 | 38 | Download [the standalone WAR file ](https://search.maven.org/remote_content?g=org.woelker.jimix.war&a=jimix&v=LATEST&e=war) and drop it inside your application server. 39 | 40 | ### WAR 41 | 42 | Maven 43 | 44 | ``` 45 | 46 | org.woelker.jimix 47 | jimix-servlet 48 | 2.1.1 49 | 50 | ``` 51 | 52 | web.xml 53 | 54 | ``` 55 | 56 | jimix 57 | org.woelker.jimix.servlet.JimixServlet 58 | 59 | 60 | 61 | jimix 62 | /jimix/* 63 | 64 | ``` 65 | 66 | ### servlet (Spring Boot) 67 | 68 | Maven 69 | 70 | 71 | ``` 72 | 73 | org.woelker.jimix 74 | jimix-servlet 75 | 2.1.1 76 | 77 | ``` 78 | 79 | Spring Boot Application 80 | 81 | ``` 82 | @Bean 83 | public ServletRegistrationBean jimixServletRegistration() { 84 | ServletRegistrationBean registration = new ServletRegistrationBean(new JimixServlet()); 85 | registration.setUrlMappings(Collections.singletonList("/jimix/*")); 86 | return registration; 87 | } 88 | ``` 89 | 90 | ### servlet (jetty) 91 | 92 | Maven 93 | 94 | 95 | ``` 96 | 97 | org.woelker.jimix 98 | jimix-servlet 99 | 2.1.1 100 | 101 | ``` 102 | 103 | Jetty 104 | 105 | ``` 106 | Server server = new Server(8080); 107 | ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); 108 | context.setContextPath("/"); 109 | server.setHandler(context); 110 | 111 | context.addServlet(new ServletHolder(new JimixServlet()), "/jimix/*"); 112 | server.start(); 113 | server.join(); 114 | ``` 115 | 116 | ### Dropwizard 117 | 118 | Maven 119 | 120 | 121 | ``` 122 | 123 | org.woelker.jimix 124 | jimix-dropwizard 125 | 2.1.1 126 | 127 | ``` 128 | 129 | Dropwizard 130 | 131 | ``` 132 | @Override 133 | public void initialize(Bootstrap bootstrap) { 134 | bootstrap.addBundle(new JimixBundle()); 135 | } 136 | ``` 137 | 138 | ### Vert.x 139 | 140 | Maven 141 | 142 | 143 | ``` 144 | 145 | org.woelker.jimix 146 | jimix-vertx 147 | 2.1.1 148 | 149 | ``` 150 | 151 | Vert.x 152 | 153 | ``` 154 | Vertx vertx = new DefaultVertx(); 155 | HttpServer server = vertx.createHttpServer(); 156 | 157 | RouteMatcher routeMatcher = new RouteMatcher(); 158 | 159 | routeMatcher.allWithRegEx("/jimix(.*)", new JimixVertxHandler()); 160 | 161 | server.requestHandler(routeMatcher).listen(8080, "localhost"); 162 | ``` 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix 7 | 2.2.1-SNAPSHOT 8 | 9 | jimix-core 10 | jar 11 | 12 | 13 | 14 | ${project.groupId} 15 | jimix-ui 16 | ${project.version} 17 | assets 18 | 19 | 20 | 21 | org.slf4j 22 | slf4j-api 23 | 1.7.7 24 | provided 25 | 26 | 27 | 28 | junit 29 | junit 30 | 4.11 31 | test 32 | jar 33 | 34 | 35 | com.fasterxml.jackson.core 36 | jackson-databind 37 | 2.1.4 38 | test 39 | jar 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/AssetRequestHandler.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | import java.io.InputStream; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | public class AssetRequestHandler implements RequestHandler { 8 | 9 | Map mimeTypeMap = new HashMap(); 10 | 11 | { 12 | mimeTypeMap.put("html", "text/html"); 13 | mimeTypeMap.put("css", "text/css"); 14 | mimeTypeMap.put("js", "application/javascript"); 15 | } 16 | 17 | @Override 18 | public void handle(HttpRequest httpRequest) throws Exception { 19 | String path = httpRequest.getPathInfo(); 20 | if ("/".equals(path)) { 21 | path = "/index.html"; 22 | } 23 | final InputStream inputStream = AssetRequestHandler.class.getResourceAsStream("/org/woelker/jimix/ui/assets" + path); 24 | if (inputStream == null) { 25 | httpRequest.sendError(HttpRequest.STATUS_NOT_FOUND); 26 | return; 27 | } 28 | try { 29 | final int lastDotIndex = path.lastIndexOf("."); 30 | final String extension = path.substring(lastDotIndex + 1); 31 | String mimeType = mimeTypeMap.get(extension); 32 | if (mimeType == null) { 33 | mimeType = "text/plain"; 34 | } 35 | httpRequest.setContentType(mimeType); 36 | JimixIOUtils.copy(inputStream, httpRequest.getOutputStream()); 37 | } finally { 38 | inputStream.close(); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/HttpRequest.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | public interface HttpRequest { 7 | 8 | public static int STATUS_NOT_FOUND = 404; 9 | 10 | public String getMethod(); 11 | 12 | public String getPathInfo(); 13 | 14 | public String getRequestURI(); 15 | 16 | public String getQueryString(); 17 | 18 | public void setContentType(String contentType); 19 | 20 | public OutputStream getOutputStream() throws IOException; 21 | 22 | public void sendError(int errorStatus) throws IOException; 23 | 24 | public void sendRedirect(String url) throws IOException; 25 | 26 | public void setAttribute(String key, Object attribute); 27 | 28 | public Object getAttribute(String key); 29 | 30 | public void setHeader(String header, String value); 31 | 32 | public void setStatus(int i) throws IOException; 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/InventoryRequestHandler.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | import java.io.IOException; 4 | import java.lang.management.ManagementFactory; 5 | import java.net.InetAddress; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.Iterator; 9 | import java.util.Map; 10 | import java.util.Set; 11 | import javax.management.MBeanAttributeInfo; 12 | import javax.management.MBeanInfo; 13 | import javax.management.MBeanServer; 14 | import javax.management.ObjectInstance; 15 | import javax.management.ObjectName; 16 | 17 | public class InventoryRequestHandler implements RequestHandler { 18 | 19 | private final String userName; 20 | private String hostName = ""; 21 | private String mainClass = ""; 22 | 23 | public InventoryRequestHandler() { 24 | userName = System.getProperty("user.name", ""); 25 | try { 26 | InetAddress iAddress = InetAddress.getLocalHost(); 27 | hostName = iAddress.getHostName(); 28 | hostName = iAddress.getCanonicalHostName();; 29 | 30 | } catch (Throwable ignored) { 31 | 32 | } 33 | mainClass = getMainClass(); 34 | } 35 | 36 | private String getMainClass() { 37 | String mainClass = ""; 38 | Map threadMap = Thread.getAllStackTraces(); 39 | for(Map.Entry entry: threadMap.entrySet()) { 40 | String name = entry.getKey().getName(); 41 | if(name.equals("main")) { 42 | StackTraceElement[] stackTraceElements = entry.getValue(); 43 | StackTraceElement stackTraceElement = stackTraceElements[stackTraceElements.length-1]; 44 | if(stackTraceElement.getMethodName().equals("main")) { 45 | return stackTraceElement.getClassName(); 46 | } 47 | } 48 | } 49 | return mainClass; 50 | } 51 | 52 | @Override 53 | public void handle(HttpRequest httpRequest) throws Exception { 54 | MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer(); 55 | 56 | Set instances = mbeanServer.queryMBeans(null, null); 57 | Map result = new HashMap(); 58 | result.put("userName", userName); 59 | result.put("hostName", hostName); 60 | result.put("mainClass", mainClass); 61 | final ArrayList mbeanList = new ArrayList(); 62 | result.put("mbeans", mbeanList); 63 | Iterator iterator = instances.iterator(); 64 | httpRequest.setContentType("application/json"); 65 | while (iterator.hasNext()) { 66 | try { 67 | ObjectInstance instance = iterator.next(); 68 | final HashMap mbean = new HashMap(); 69 | final ObjectName objectName = instance.getObjectName(); 70 | final MBeanInfo mBeanInfo = mbeanServer.getMBeanInfo(objectName); 71 | final String description = mBeanInfo.getDescription(); 72 | mbean.put("objectName", objectName.getCanonicalName()); 73 | mbean.put("description", description); 74 | final ArrayList attributes = new ArrayList(); 75 | mbean.put("attributes", attributes); 76 | mbeanList.add(mbean); 77 | for (MBeanAttributeInfo attributeInfo : mBeanInfo.getAttributes()) { 78 | final HashMap attribute = new HashMap(); 79 | attribute.put("name", attributeInfo.getName()); 80 | attribute.put("type", attributeInfo.getType()); 81 | attribute.put("description", attributeInfo.getDescription()); 82 | attributes.add(attribute); 83 | } 84 | } catch (Throwable throwable) { 85 | // TODO: how to handle? 86 | } 87 | 88 | } 89 | new JsonWriter(httpRequest.getOutputStream()).serialize(result); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/JimixIOUtils.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | public class JimixIOUtils { 8 | 9 | public static void copy(InputStream input, OutputStream output) { 10 | try { 11 | byte[] buffer = new byte[4096]; 12 | int len; 13 | while ((len = input.read(buffer)) != -1) { 14 | output.write(buffer, 0, len); 15 | } 16 | } catch (IOException ex) { 17 | throw new RuntimeException(ex); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/JimixRequestHandler.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | import javax.management.MBeanInfo; 9 | import javax.management.ObjectInstance; 10 | import org.woelker.jimix.core.logging.Logger; 11 | import org.woelker.jimix.core.logging.LoggerFactory; 12 | 13 | public class JimixRequestHandler implements RequestHandler { 14 | 15 | private final Logger logger = LoggerFactory.createLogger(JimixRequestHandler.class); 16 | 17 | public static class DispatchEntry { 18 | 19 | Pattern pattern; 20 | RequestHandler handler; 21 | 22 | public DispatchEntry(Pattern pattern, RequestHandler handler) { 23 | this.pattern = pattern; 24 | this.handler = handler; 25 | } 26 | 27 | } 28 | ArrayList dispatchList = new ArrayList(); 29 | 30 | public JimixRequestHandler() { 31 | addDispatch("/api/inventory", new InventoryRequestHandler()); 32 | addDispatch("/api/mbeans/([^/]+)", new MbeanRequestHandler()); 33 | addDispatch("/api/mbeans/([^/]+)/(.+)", new MbeanOperationRequestHandler()); 34 | addDispatch(".*", new AssetRequestHandler()); 35 | logger.info("Created jimix request handler"); 36 | } 37 | 38 | @Override 39 | public void handle(HttpRequest httpRequest) throws Exception { 40 | try { 41 | httpRequest.setHeader("Access-Control-Allow-Origin", "*"); 42 | if (httpRequest.getPathInfo() == null || httpRequest.getPathInfo().length() == 0) { 43 | httpRequest.sendRedirect(httpRequest.getRequestURI() + "/"); 44 | return; 45 | } 46 | String path = httpRequest.getPathInfo(); 47 | for (DispatchEntry dispatchEntry : dispatchList) { 48 | final Matcher matcher = dispatchEntry.pattern.matcher(path); 49 | if (matcher.matches()) { 50 | for (int i = 0; i < matcher.groupCount(); i++) { 51 | httpRequest.setAttribute("param-" + i, matcher.group(i + 1)); 52 | } 53 | dispatchEntry.handler.handle(httpRequest); 54 | return; 55 | } 56 | } 57 | } catch (Throwable throwable) { 58 | logger.warn("Error handling request", throwable); 59 | httpRequest.setStatus(500); 60 | Map result = new HashMap(); 61 | result.put("message", throwable.getMessage()); 62 | new JsonWriter(httpRequest.getOutputStream()).serialize(result); 63 | 64 | } 65 | } 66 | 67 | private void addDispatch(String regex, RequestHandler handler) { 68 | dispatchList.add(new DispatchEntry(Pattern.compile("^" + regex + "$"), handler)); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/JsonWriter.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | import java.io.OutputStream; 4 | import java.io.PrintWriter; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | import javax.management.ObjectName; 11 | 12 | class JsonWriter { 13 | 14 | private final OutputStream outputStream; 15 | private final Pattern quotePattern = Pattern.compile("[\"]"); 16 | private final String quoteReplacement = Matcher.quoteReplacement("\\\""); 17 | private final Pattern backslashPattern = Pattern.compile("[\\\\]"); 18 | private final String backslashReplacement = Matcher.quoteReplacement("\\\\"); 19 | private final Pattern newlinePattern = Pattern.compile("\n"); 20 | private final String newlineReplacement = Matcher.quoteReplacement("\\n"); 21 | 22 | public JsonWriter(OutputStream outputStream) { 23 | this.outputStream = outputStream; 24 | writer = new PrintWriter(outputStream); 25 | } 26 | private final PrintWriter writer; 27 | 28 | void serialize(Object object) { 29 | serialize(object, 0); 30 | writer.flush(); 31 | } 32 | 33 | private void serialize(Object object, int indentation) { 34 | if (object instanceof String) { 35 | serializeString((String) object); 36 | } else if (object instanceof Map) { 37 | serializeMap((Map) object, indentation); 38 | } else if (object instanceof List) { 39 | serializeList((List) object, indentation); 40 | } else if (object instanceof Object[]) { 41 | serializeList(Arrays.asList((Object[])object), indentation); 42 | } else if (object == null) { 43 | writer.print("null"); 44 | } else if (object instanceof Number) { 45 | serializeNumber((Number) object); 46 | } else if (object instanceof Boolean) { 47 | writer.print(object.toString()); 48 | } else if (object instanceof ObjectName) { 49 | serializeString(object.toString()); 50 | } else { 51 | serializeString(object.toString()); 52 | } 53 | 54 | } 55 | 56 | private void serializeNumber(Number number) { 57 | double doubleValue = number.doubleValue(); 58 | if(Double.isInfinite(doubleValue) || Double.isNaN(doubleValue)) { 59 | writer.print("null"); 60 | } else { 61 | writer.print(Double.toString(doubleValue)); 62 | } 63 | } 64 | 65 | private void serializeMap(Map map, int indentation) { 66 | writer.print("{"); 67 | String comma = ""; 68 | for (Map.Entry value : map.entrySet()) { 69 | writer.println(comma); 70 | indent(indentation + 1); 71 | writer.print("\""); 72 | writer.print(value.getKey().toString()); 73 | writer.print("\": "); 74 | serialize(value.getValue(), indentation + 1); 75 | comma = ","; 76 | } 77 | writer.println(); 78 | indent(indentation); 79 | writer.print("}"); 80 | } 81 | 82 | private void indent(int indentation) { 83 | for (int i = 0; i < indentation; i++) { 84 | writer.print("\t"); 85 | } 86 | } 87 | 88 | private void serializeList(List list, int indentation) { 89 | writer.print("["); 90 | String comma = ""; 91 | for (Object value : list) { 92 | writer.println(comma); 93 | indent(indentation + 1); 94 | serialize(value, indentation + 1); 95 | comma = ","; 96 | } 97 | writer.println(); 98 | indent(indentation); 99 | writer.print("]"); 100 | } 101 | 102 | private void serializeString(String string) { 103 | writer.print("\""); 104 | String backslashesReplaced = backslashPattern.matcher(string).replaceAll(backslashReplacement); 105 | String quotesReplaced = quotePattern.matcher(backslashesReplaced).replaceAll(quoteReplacement); 106 | String newlinesReplaced = newlinePattern.matcher(quotesReplaced).replaceAll(newlineReplacement); 107 | writer.print(newlinesReplaced); 108 | writer.print("\""); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/MbeanOperationRequestHandler.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | import java.lang.management.ManagementFactory; 4 | import java.net.URLDecoder; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Set; 10 | import javax.management.*; 11 | 12 | public class MbeanOperationRequestHandler implements RequestHandler { 13 | 14 | MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer(); 15 | 16 | TypeCoercer typeCoercer = new TypeCoercer(); 17 | 18 | @Override 19 | public void handle(HttpRequest httpRequest) throws Exception { 20 | if ("PUT".equals(httpRequest.getMethod())) { 21 | handleSetAttribute(httpRequest); 22 | return; 23 | } 24 | String objectName = (String) httpRequest.getAttribute("param-0"); 25 | objectName = URLDecoder.decode(objectName, "UTF-8"); 26 | final String operationName = (String) httpRequest.getAttribute("param-1"); 27 | 28 | List arguments = new ArrayList(); 29 | 30 | final String queryString = httpRequest.getQueryString(); 31 | if (queryString != null) { 32 | String[] pairs = queryString.split("&"); 33 | for (String pair : pairs) { 34 | int idx = pair.indexOf("="); 35 | final String name = URLDecoder.decode(pair.substring(0, idx), "UTF-8"); 36 | final String value = URLDecoder.decode(pair.substring(idx + 1), "UTF-8"); 37 | if ("argument".equals(name)) { 38 | arguments.add(value); 39 | } 40 | 41 | } 42 | } 43 | Set instances = mbeanServer.queryMBeans(new ObjectName(objectName), null); 44 | if (instances.isEmpty()) { 45 | httpRequest.sendError(HttpRequest.STATUS_NOT_FOUND); 46 | return; 47 | } 48 | ObjectInstance instance = instances.iterator().next(); 49 | MBeanInfo mBeanInfo = mbeanServer.getMBeanInfo(instance.getObjectName()); 50 | String[] signature = new String[0]; 51 | for (MBeanOperationInfo operation : mBeanInfo.getOperations()) { 52 | if (arguments.size() == operation.getSignature().length) { 53 | List parameters = new ArrayList(); 54 | for (MBeanParameterInfo parameter : operation.getSignature()) { 55 | parameters.add(parameter.getType()); 56 | } 57 | signature = parameters.toArray(signature); 58 | break; 59 | } 60 | } 61 | Object[] argumentArray = new Object[signature.length]; 62 | for (int i = 0; i < signature.length; i++) { 63 | final Object value = typeCoercer.coerce(arguments.get(i), signature[i]); 64 | argumentArray[i] = value; 65 | } 66 | Object invocationResult = mbeanServer.invoke(instance.getObjectName(), operationName, argumentArray, signature); 67 | Map result = new HashMap(); 68 | result.put("result", invocationResult); 69 | new JsonWriter(httpRequest.getOutputStream()).serialize(result); 70 | } 71 | 72 | private void handleSetAttribute(HttpRequest httpRequest) throws Exception { 73 | final String objectName = (String) httpRequest.getAttribute("param-0"); 74 | final String attributeName = (String) httpRequest.getAttribute("param-1"); 75 | String newValueString = null; 76 | final String queryString = httpRequest.getQueryString(); 77 | if (queryString != null) { 78 | String[] pairs = queryString.split("&"); 79 | for (String pair : pairs) { 80 | int idx = pair.indexOf("="); 81 | final String name = URLDecoder.decode(pair.substring(0, idx), "UTF-8"); 82 | final String value = URLDecoder.decode(pair.substring(idx + 1), "UTF-8"); 83 | if ("value".equals(name)) { 84 | newValueString = value; 85 | } 86 | 87 | } 88 | } 89 | Set instances = mbeanServer.queryMBeans(new ObjectName(objectName), null); 90 | if (instances.isEmpty()) { 91 | httpRequest.sendError(HttpRequest.STATUS_NOT_FOUND); 92 | return; 93 | } 94 | ObjectInstance instance = instances.iterator().next(); 95 | Object newValue = null; 96 | MBeanInfo mBeanInfo = mbeanServer.getMBeanInfo(instance.getObjectName()); 97 | for (MBeanAttributeInfo attribute : mBeanInfo.getAttributes()) { 98 | if (attributeName.equals(attribute.getName())) { 99 | newValue = typeCoercer.coerce(newValueString, attribute.getType()); 100 | break; 101 | } 102 | } 103 | mbeanServer.setAttribute(instance.getObjectName(), new Attribute(attributeName, newValue)); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/MbeanRequestHandler.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | import java.lang.management.ManagementFactory; 4 | import java.net.URLDecoder; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import javax.management.*; 10 | 11 | public class MbeanRequestHandler implements RequestHandler { 12 | MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer(); 13 | 14 | @Override 15 | public void handle(HttpRequest httpRequest) throws Exception { 16 | String objectName = (String) httpRequest.getAttribute("param-0"); 17 | objectName = URLDecoder.decode(objectName, "UTF-8"); 18 | Set instances = mbeanServer.queryMBeans(new ObjectName(objectName), null); 19 | if (instances.isEmpty()) { 20 | httpRequest.sendError(HttpRequest.STATUS_NOT_FOUND); 21 | return; 22 | } 23 | Map result = new HashMap(); 24 | ObjectInstance instance = instances.iterator().next(); 25 | MBeanInfo mBeanInfo = mbeanServer.getMBeanInfo(instance.getObjectName()); 26 | final ArrayList attributes = new ArrayList(); 27 | result.put("attributes", attributes); 28 | for (MBeanAttributeInfo attributeInfo : mBeanInfo.getAttributes()) { 29 | final HashMap attribute = new HashMap(); 30 | attribute.put("name", attributeInfo.getName()); 31 | attribute.put("type", attributeInfo.getType()); 32 | attribute.put("description", attributeInfo.getDescription()); 33 | attribute.put("writable", attributeInfo.isWritable()); 34 | try { 35 | attribute.put("value", mbeanServer.getAttribute(instance.getObjectName(), attributeInfo.getName())); 36 | } catch (RuntimeMBeanException ignored) { 37 | attribute.put("value", null); 38 | } 39 | attributes.add(attribute); 40 | } 41 | final ArrayList operations = new ArrayList(); 42 | result.put("operations", operations); 43 | for (MBeanOperationInfo operationInfo : mBeanInfo.getOperations()) { 44 | final HashMap operation = new HashMap(); 45 | operation.put("name", operationInfo.getName()); 46 | operation.put("returnType", operationInfo.getReturnType()); 47 | operation.put("description", operationInfo.getDescription()); 48 | ArrayList signature = new ArrayList(); 49 | operation.put("signature", signature); 50 | for(MBeanParameterInfo parameterInfo: operationInfo.getSignature()) { 51 | final HashMap parameter = new HashMap(); 52 | parameter.put("name", parameterInfo.getName()); 53 | parameter.put("type", parameterInfo.getType()); 54 | parameter.put("description", parameterInfo.getDescription()); 55 | signature.add(parameter); 56 | } 57 | operations.add(operation); 58 | } 59 | 60 | new JsonWriter(httpRequest.getOutputStream()).serialize(result); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/RequestHandler.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | public interface RequestHandler { 4 | 5 | public void handle(HttpRequest httpRequest) throws Exception; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/TypeCoercer.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | class TypeCoercer { 8 | static Map> primitiveTypeMap = new HashMap>(); 9 | static { 10 | primitiveTypeMap.put("byte", Byte.class); 11 | primitiveTypeMap.put("short", short.class); 12 | primitiveTypeMap.put("char", Character.class); 13 | primitiveTypeMap.put("int", Integer.class); 14 | primitiveTypeMap.put("long", Long.class); 15 | primitiveTypeMap.put("float", Float.class); 16 | primitiveTypeMap.put("double", Double.class); 17 | primitiveTypeMap.put("boolean", Boolean.class); 18 | } 19 | 20 | Object coerce(String string, Class cls) { 21 | try { 22 | if(cls == String.class) { 23 | return string; 24 | } 25 | final Constructor stringConstructor = cls.getConstructor(String.class); 26 | if(stringConstructor != null) { 27 | return stringConstructor.newInstance(string); 28 | } 29 | throw new RuntimeException("Unable to coerce '"+string+"' to class "+cls.getName()); 30 | } catch (Exception ex) { 31 | throw new RuntimeException("Unable to coerce '"+string+"' to class "+cls.getName(), ex); 32 | } 33 | } 34 | 35 | Object coerce(String input, String className) { 36 | Class cls = classForName(className); 37 | return coerce(input, cls); 38 | } 39 | 40 | private Class classForName(String className) { 41 | try { 42 | Class cls = primitiveTypeMap.get(className); 43 | if(cls != null) { 44 | return cls; 45 | } 46 | return Class.forName(className); 47 | } catch (ClassNotFoundException ex) { 48 | throw new RuntimeException(ex); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/logging/JuliLogger.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core.logging; 2 | 3 | import java.util.logging.Level; 4 | 5 | class JuliLogger implements Logger { 6 | 7 | protected final java.util.logging.Logger delegate; 8 | 9 | JuliLogger(Class clazz) { 10 | delegate = java.util.logging.Logger.getLogger(clazz.getName()); 11 | } 12 | 13 | @Override 14 | public void info(String logMessage) { 15 | log(Level.INFO, logMessage); 16 | } 17 | 18 | @Override 19 | public void debug(String logMessage) { 20 | log(Level.FINE, logMessage); 21 | } 22 | 23 | @Override 24 | public void warn(String logMessage) { 25 | log(Level.WARNING, logMessage); 26 | } 27 | 28 | @Override 29 | public void warn(String logMessage, Throwable throwable) { 30 | log(Level.WARNING, logMessage, throwable); 31 | } 32 | 33 | @Override 34 | public void error(String logMessage) { 35 | log(Level.SEVERE, logMessage); 36 | } 37 | 38 | @Override 39 | public void error(String logMessage, Throwable throwable) { 40 | log(Level.SEVERE, logMessage, throwable); 41 | } 42 | 43 | private void log(Level level, String logMessage, Throwable throwable) { 44 | if (delegate.isLoggable(level)) { 45 | final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 46 | final StackTraceElement element = stackTrace[METHODNAME_DEPTH]; 47 | final String methodName = element.getMethodName(); 48 | final String className = element.getClassName(); 49 | delegate.logp(level, className, methodName, logMessage, throwable); 50 | } 51 | } 52 | 53 | private void log(Level level, String logMessage) { 54 | if (delegate.isLoggable(level)) { 55 | final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 56 | final StackTraceElement element = stackTrace[METHODNAME_DEPTH]; 57 | final String methodName = element.getMethodName(); 58 | final String className = element.getClassName(); 59 | delegate.logp(level, className, methodName, logMessage); 60 | } 61 | } 62 | private static final int METHODNAME_DEPTH = 3; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/logging/Logger.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core.logging; 2 | 3 | public interface Logger { 4 | 5 | public void info(String logMessage); 6 | 7 | public void debug(String logMessage); 8 | 9 | public void warn(String logMessage); 10 | 11 | public void warn(String logMessage, Throwable throwable); 12 | 13 | public void error(String logMessage); 14 | 15 | public void error(String logMessage, Throwable throwable); 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/logging/LoggerFactory.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core.logging; 2 | 3 | public class LoggerFactory { 4 | 5 | static boolean hasSlf4j = false; 6 | 7 | static { 8 | try { 9 | final Class clazz = LoggerFactory.class.getClassLoader().loadClass("org.slf4j.Logger"); 10 | if (clazz != null) { 11 | hasSlf4j = true; 12 | } 13 | } catch (Throwable ignored) { 14 | 15 | } 16 | 17 | } 18 | 19 | public static Logger createLogger(Class clazz) { 20 | if (hasSlf4j) { 21 | return new Slf4jLogger(clazz); 22 | } else { 23 | return new JuliLogger(clazz); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/org/woelker/jimix/core/logging/Slf4jLogger.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core.logging; 2 | 3 | class Slf4jLogger implements Logger { 4 | 5 | protected final org.slf4j.Logger delegate; 6 | 7 | Slf4jLogger(Class clazz) { 8 | delegate = org.slf4j.LoggerFactory.getLogger(clazz); 9 | } 10 | 11 | @Override 12 | public void info(String logMessage) { 13 | delegate.info(logMessage); 14 | } 15 | 16 | @Override 17 | public void debug(String logMessage) { 18 | delegate.debug(logMessage); 19 | } 20 | 21 | @Override 22 | public void warn(String logMessage) { 23 | delegate.warn(logMessage); 24 | } 25 | 26 | @Override 27 | public void warn(String logMessage, Throwable throwable) { 28 | delegate.warn(logMessage, throwable); 29 | } 30 | 31 | @Override 32 | public void error(String logMessage) { 33 | delegate.error(logMessage); 34 | } 35 | 36 | @Override 37 | public void error(String logMessage, Throwable throwable) { 38 | delegate.error(logMessage, throwable); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /core/src/test/java/org/woelker/jimix/core/JsonWriterTest.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import java.io.ByteArrayInputStream; 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.Serializable; 8 | import java.util.Arrays; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | 12 | import static org.junit.Assert.*; 13 | import org.junit.Test; 14 | 15 | public class JsonWriterTest { 16 | 17 | @Test 18 | public void serializeNull() throws Exception { 19 | verifyRoundTrip(null); 20 | } 21 | 22 | @Test 23 | public void serializeString() throws Exception { 24 | verifyRoundTrip("foo"); 25 | } 26 | 27 | @Test 28 | public void serializeStringWithQuotes() throws Exception { 29 | verifyRoundTrip("\"foo\""); 30 | } 31 | 32 | @Test 33 | public void serializeStringWithBackslash() throws Exception { 34 | verifyRoundTrip("\\foo"); 35 | } 36 | 37 | @Test 38 | public void serializeStringWithQuoteAndBackslash() throws Exception { 39 | verifyRoundTrip("\\foo\""); 40 | } 41 | 42 | @Test 43 | public void serializeStringWithNewline() throws Exception { 44 | verifyRoundTrip("fo\no"); 45 | } 46 | 47 | @Test 48 | public void serializeDouble() throws Exception { 49 | verifyRoundTrip(1.25); 50 | } 51 | 52 | @Test 53 | public void serializeDoubleMaxValue() throws Exception { 54 | verifyRoundTrip(Double.MAX_VALUE); 55 | } 56 | 57 | @Test 58 | public void serializeDoubleMinValue() throws Exception { 59 | verifyRoundTrip(Double.MIN_VALUE); 60 | } 61 | 62 | @Test 63 | public void serializeNan() throws Exception { 64 | verify(Double.NaN, null); 65 | } 66 | 67 | @Test 68 | public void serializePositiveInfinity() throws Exception { 69 | verify(Double.POSITIVE_INFINITY, null); 70 | } 71 | 72 | @Test 73 | public void serializeNegativeInfinity() throws Exception { 74 | verify(Double.NEGATIVE_INFINITY, null); 75 | } 76 | 77 | @Test 78 | public void serializeBigDouble() throws Exception { 79 | verifyRoundTrip(1.25e44); 80 | } 81 | 82 | @Test 83 | public void serializeSmallDouble() throws Exception { 84 | verifyRoundTrip(-1.25e-44); 85 | } 86 | 87 | @Test 88 | public void serializeTrue() throws Exception { 89 | verifyRoundTrip(true); 90 | } 91 | 92 | @Test 93 | public void serializeFalse() throws Exception { 94 | verifyRoundTrip(false); 95 | } 96 | 97 | @Test 98 | public void serializeList() throws Exception { 99 | verifyRoundTrip(Arrays.asList(1.0, "foo", null, true, false, "bar")); 100 | } 101 | 102 | @Test 103 | public void serializeArray() throws Exception { 104 | List list = Arrays.asList(1.0, "foo", null, true, false, "bar"); 105 | verify(list.toArray(), list); 106 | } 107 | 108 | @Test 109 | public void serializeMap() throws Exception { 110 | final HashMap map = new HashMap(); 111 | map.put("foo", "bar"); 112 | map.put("foo2", 1.0); 113 | map.put("foo3", false); 114 | map.put("foo4", Arrays.asList(1.0, "foo", null, true, false, "bar")); 115 | verifyRoundTrip(map); 116 | } 117 | 118 | @Test 119 | public void serializeComplex() throws Exception { 120 | final HashMap map = new HashMap(); 121 | map.put("foo", "bar"); 122 | map.put("foo2", 1.0); 123 | map.put("foo3", false); 124 | map.put("foo4", Arrays.asList(1.0, "foo", null, true, false, "bar")); 125 | verifyRoundTrip(Arrays.asList(1.0, "foo", null, true, false, "bar", map)); 126 | } 127 | 128 | @Test 129 | public void serializeUnknown() throws Exception { 130 | verify(new Object() { 131 | public String toString() { 132 | return "foo"; 133 | } 134 | }, "foo"); 135 | } 136 | 137 | private void verifyRoundTrip(T original) throws Exception { 138 | final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 139 | new JsonWriter(baos).serialize(original); 140 | ObjectMapper mapper = new ObjectMapper(); 141 | T roundTripped = mapper.readValue(new ByteArrayInputStream(baos.toByteArray()), 142 | new TypeReference() { 143 | }); 144 | 145 | assertEquals(original, roundTripped); 146 | 147 | } 148 | 149 | private void verify(Object original, Object expected) throws Exception { 150 | final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 151 | new JsonWriter(baos).serialize(original); 152 | ObjectMapper mapper = new ObjectMapper(); 153 | Object roundTripped = mapper.readValue(new ByteArrayInputStream(baos.toByteArray()), 154 | new TypeReference() { 155 | }); 156 | 157 | assertEquals(expected, roundTripped); 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /core/src/test/java/org/woelker/jimix/core/TypeCoercerTest.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.core; 2 | 3 | import static org.junit.Assert.*; 4 | import org.junit.Rule; 5 | import org.junit.Test; 6 | import org.junit.rules.ExpectedException; 7 | 8 | public class TypeCoercerTest { 9 | 10 | @Rule 11 | public ExpectedException expectedException = ExpectedException.none(); 12 | 13 | TypeCoercer typeCoercer = new TypeCoercer(); 14 | 15 | @Test 16 | public void testString1() { 17 | verify("", "", String.class); 18 | } 19 | 20 | @Test 21 | public void testString2() { 22 | verify("abc", "abc", String.class); 23 | } 24 | 25 | @Test 26 | public void testInteger() { 27 | verify(1, "1", Integer.class); 28 | } 29 | 30 | @Test 31 | public void testFloat() { 32 | verify(1f, "1", Float.class); 33 | } 34 | 35 | @Test 36 | public void testBooleanTrue() { 37 | verify(true, "true", Boolean.class); 38 | } 39 | 40 | @Test 41 | public void testBooleanFalse() { 42 | verify(false, "false", Boolean.class); 43 | } 44 | 45 | @Test 46 | public void testInt() { 47 | verify(42, "42", "int"); 48 | } 49 | 50 | @Test 51 | public void testIntFail() { 52 | expectedException.expectMessage("Unable to coerce 'asf' to class java.lang.Integer"); 53 | typeCoercer.coerce("asf", "int"); 54 | } 55 | 56 | private void verify(Object expected, String input, Class aClass) { 57 | assertEquals(expected, typeCoercer.coerce(input, aClass)); 58 | } 59 | 60 | private void verify(Object expected, String input, String className) { 61 | assertEquals(expected, typeCoercer.coerce(input, className)); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /dropwizard/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix 7 | 2.2.1-SNAPSHOT 8 | 9 | jimix-dropwizard 10 | 11 | 12 | 0.7.0 13 | 14 | 15 | 16 | 17 | ${project.groupId} 18 | jimix-servlet 19 | ${project.version} 20 | 21 | 22 | 23 | io.dropwizard 24 | dropwizard-core 25 | ${dropwizard.version} 26 | 27 | 28 | -------------------------------------------------------------------------------- /dropwizard/src/main/java/org/woelker/jimix/dropwizard/JimixBundle.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.dropwizard; 2 | 3 | import io.dropwizard.Bundle; 4 | import io.dropwizard.setup.Bootstrap; 5 | import io.dropwizard.setup.Environment; 6 | import org.woelker.jimix.servlet.JimixServlet; 7 | 8 | public class JimixBundle implements Bundle { 9 | 10 | @Override 11 | public void initialize(Bootstrap bootstrap) { 12 | 13 | } 14 | 15 | @Override 16 | public void run(Environment environment) { 17 | environment.servlets().addServlet("/jimix", new JimixServlet()).addMapping("/jimix/*"); 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /how-to-release.txt: -------------------------------------------------------------------------------- 1 | mvn -N versions:update-child-modules -DgenerateBackupPoms=false 2 | 3 | mvn release:clean 4 | mvn release:prepare -DautoVersionSubmodules=true 5 | mvn release:perform 6 | 7 | https://oss.sonatype.org/ 8 | 9 | https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.woelker.jimix 5 | jimix 6 | 2.2.1-SNAPSHOT 7 | pom 8 | 9 | A modern JMX web console 10 | https://github.com/manuel-woelker/jimix 11 | 12 | 13 | org.sonatype.oss 14 | oss-parent 15 | 7 16 | 17 | 18 | 19 | 20 | The Apache Software License, Version 2.0 21 | http://www.apache.org/licenses/LICENSE-2.0.txt 22 | repo 23 | 24 | 25 | 26 | scm:git:https://github.com/manuel-woelker/jimix.git 27 | scm:git:https://github.com/manuel-woelker/jimix.git 28 | scm:git:git@github.com:manuel-woelker/jimix.git 29 | 30 | 31 | 32 | mwoelker 33 | Manuel Woelker 34 | jimix@manuel.woelker.org 35 | 36 | 37 | 38 | 39 | UTF-8 40 | 1.6 41 | 1.6 42 | 43 | 44 | ui 45 | core 46 | servlet 47 | dropwizard 48 | vertx 49 | war 50 | samples 51 | 52 | -------------------------------------------------------------------------------- /samples/dropwizard/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix-samples 7 | 2.2.1-SNAPSHOT 8 | 9 | jimix-samples-dropwizard 10 | jar 11 | 12 | 13 | 14 | ${project.groupId} 15 | jimix-dropwizard 16 | ${project.version} 17 | 18 | 19 | ch.qos.logback 20 | logback-classic 21 | 1.1.2 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /samples/dropwizard/src/main/java/org/woelker/jimix/samples/dropwizard/JimixSampleDropwizard.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.samples.dropwizard; 2 | 3 | import io.dropwizard.Configuration; 4 | import io.dropwizard.setup.Bootstrap; 5 | import io.dropwizard.setup.Environment; 6 | import io.dropwizard.Application; 7 | import org.woelker.jimix.dropwizard.JimixBundle; 8 | 9 | 10 | public class JimixSampleDropwizard extends Application { 11 | public static void main(String[] args) throws Exception { 12 | if (args.length == 0) { 13 | args = new String[]{"server"}; 14 | } 15 | new JimixSampleDropwizard().run(args); 16 | } 17 | 18 | @Override 19 | public void initialize(Bootstrap bootstrap) { 20 | bootstrap.addBundle(new JimixBundle()); 21 | } 22 | 23 | @Override 24 | public void run(Configuration configuration, 25 | Environment environment) { 26 | // disable jersey to prevent errors due to missing resources 27 | environment.jersey().disable(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /samples/jetty/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix-samples 7 | 2.2.1-SNAPSHOT 8 | 9 | jimix-samples-jetty 10 | jar 11 | 12 | 13 | 14 | ${project.groupId} 15 | jimix-servlet 16 | ${project.version} 17 | 18 | 19 | org.eclipse.jetty 20 | jetty-server 21 | 9.1.0.v20131115 22 | 23 | 24 | org.eclipse.jetty 25 | jetty-servlet 26 | 9.1.0.v20131115 27 | 28 | 29 | com.yammer.metrics 30 | metrics-core 31 | 2.2.0 32 | 33 | 34 | ch.qos.logback 35 | logback-classic 36 | 1.1.2 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /samples/jetty/src/main/java/org/woelker/jimix/samples/jetty/JimixSampleJetty.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.samples.jetty; 2 | 3 | import com.yammer.metrics.Metrics; 4 | import com.yammer.metrics.core.Gauge; 5 | import com.yammer.metrics.core.MetricName; 6 | import org.eclipse.jetty.server.Server; 7 | import org.eclipse.jetty.servlet.ServletContextHandler; 8 | import org.eclipse.jetty.servlet.ServletHolder; 9 | import org.woelker.jimix.servlet.JimixServlet; 10 | 11 | import javax.management.*; 12 | import java.lang.management.ManagementFactory; 13 | import java.util.Arrays; 14 | import java.util.Date; 15 | import java.util.logging.Level; 16 | import java.util.logging.Logger; 17 | 18 | public class JimixSampleJetty { 19 | 20 | public static void main(String[] args) throws Exception { 21 | new JimixSampleJetty().run(); 22 | } 23 | 24 | private void run() throws Exception { 25 | addSampleMetrics(); 26 | ManagementFactory.getPlatformMBeanServer().registerMBean(new Hello(), new ObjectName("asdf:name=bar")); 27 | 28 | Server server = new Server(8080); 29 | ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); 30 | context.setContextPath("/"); 31 | server.setHandler(context); 32 | 33 | context.addServlet(new ServletHolder(new JimixServlet()), "/jimix/*"); 34 | server.start(); 35 | server.join(); 36 | 37 | 38 | } 39 | 40 | 41 | public static class Hello implements HelloMBean { 42 | 43 | private String message = "Hello World"; 44 | 45 | @Override 46 | public String getMessage() { 47 | return this.message; 48 | } 49 | 50 | @Override 51 | public void sayHello() { 52 | System.out.println(message); 53 | } 54 | 55 | @Override 56 | public String createGreeting() { 57 | System.out.println("Creating greeting"); 58 | return this.message; 59 | } 60 | 61 | @Override 62 | public void alwaysFails() { 63 | throw new RuntimeException("always broken"); 64 | } 65 | 66 | @Override 67 | public String takes3seconds() throws InterruptedException { 68 | Thread.sleep(3000); 69 | return this.message; 70 | } 71 | 72 | @Override 73 | public void setMessage(String message) { 74 | System.out.println("message was set to "+message); 75 | try { 76 | Thread.sleep(500); 77 | } catch (InterruptedException ex) { 78 | throw new RuntimeException(ex); 79 | } 80 | this.message = message; 81 | } 82 | 83 | @Override 84 | public String takesLotsOfParameters(String string, Integer integer, Float f, Boolean b) { 85 | System.out.println("string: "+ string); 86 | System.out.println("int: "+ integer); 87 | System.out.println("float: "+ f); 88 | System.out.println("boolean: "+ b); 89 | return "Success"; 90 | } 91 | 92 | @Override 93 | public String getReadOnly() { 94 | return "readonly"; 95 | } 96 | 97 | @Override 98 | public String getMessageError() { 99 | return message; 100 | } 101 | 102 | @Override 103 | public void setMessageError(String message) { 104 | throw new RuntimeException("always fail"); 105 | } 106 | 107 | @Override 108 | public String getDate() { 109 | return (new Date()).toString(); 110 | } 111 | 112 | @Override 113 | public long getTimeInMillis() { 114 | return System.currentTimeMillis(); 115 | } 116 | 117 | @Override 118 | public double getRandom() { 119 | return Math.random(); 120 | } 121 | 122 | @Override 123 | public boolean getBoolean() { 124 | return Math.random() > 0.5; 125 | } 126 | 127 | } 128 | 129 | public static interface HelloMBean { 130 | 131 | // operations 132 | 133 | public void sayHello(); 134 | public String createGreeting(); 135 | public void alwaysFails(); 136 | public String takes3seconds() throws InterruptedException; 137 | 138 | public String takesLotsOfParameters(String string, Integer integer, Float f, Boolean b); 139 | 140 | // attributes 141 | 142 | // a read-write attribute called Message of type String 143 | public String getMessage(); 144 | public void setMessage(String message); 145 | public String getReadOnly(); 146 | public String getMessageError(); 147 | public void setMessageError(String message); 148 | public String getDate(); 149 | public long getTimeInMillis(); 150 | public double getRandom(); 151 | public boolean getBoolean(); 152 | 153 | } 154 | 155 | private void addSampleMetrics() { 156 | Metrics.newGauge(JimixSampleJetty.class, "/foo", new Gauge() { 157 | long current = 0; 158 | 159 | @Override 160 | public Long value() { 161 | return current++; 162 | } 163 | }); 164 | Metrics.newGauge(JimixSampleJetty.class, "foo", "bar", new Gauge() { 165 | long current = 0; 166 | 167 | @Override 168 | public Long value() { 169 | return current++; 170 | } 171 | }); 172 | final MetricName metricName = new MetricName("fizzfizzfizzfizzfizzfizzfizzfizz fizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizz fizzfizzfizzfizzfizzfizzfizzfizz", "buzz", "foofoofoofoofoofoofoofoofoofoofoo foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo foofoofoofoofoofoo"); 173 | Metrics.newGauge(metricName, new Gauge() { 174 | long current = 0; 175 | 176 | @Override 177 | public Long value() { 178 | return current++; 179 | } 180 | }); 181 | final MetricName metricNameString = new MetricName("fizz", "buzz", "foo"); 182 | Metrics.newGauge(metricNameString, new Gauge() { 183 | @Override 184 | public String value() { 185 | return "fizzfizzfizzfizzfizzfizzfizzfizz fizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizz fizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizz fizzfizzfizzfizzfizzfizzfizzfizz fizzfizzfizzfizzfizzfizzfizzfizz"; 186 | } 187 | }); 188 | final MetricName metricNameArray = new MetricName("fizz", "buzz", "array"); 189 | Metrics.newGauge(metricNameArray, new Gauge() { 190 | @Override 191 | public String[] value() { 192 | return Arrays.asList("fizzfizzfizzfizzfizzfizzfizzfizz", "fizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizz", "fizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizzfizz", "fizzfizzfizzfizzfizzfizzfizzfizz", "fizzfizzfizzfizzfizzfizzfizzfizz").toArray(new String[0]); 193 | } 194 | }); 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /samples/jetty/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /samples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix 7 | 2.2.1-SNAPSHOT 8 | 9 | jimix-samples 10 | pom 11 | 12 | 13 | jetty 14 | war 15 | dropwizard 16 | vertx 17 | 18 | 19 | -------------------------------------------------------------------------------- /samples/vertx/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix-samples 7 | 2.2.1-SNAPSHOT 8 | 9 | 10 | jimix-samples-vertx 11 | jar 12 | 13 | 14 | 17 | false 18 | 19 | 21 | false 22 | 23 | 24 | ${project.groupId}~${project.artifactId}~${project.version} 25 | 26 | 28 | target/mods 29 | 30 | 31 | 2.1RC2 32 | 2.0.3-final 33 | 4.11 34 | 35 | 36 | 3.0 37 | 2.6 38 | 2.5 39 | 2.0.5-final 40 | 2.14 41 | 2.14 42 | 2.14 43 | 2.9 44 | 2.7 45 | 46 | 47 | 48 | 49 | ${project.groupId} 50 | jimix-vertx 51 | ${project.version} 52 | 53 | 54 | 55 | 56 | 57 | io.vertx 58 | vertx-core 59 | ${vertx.version} 60 | 61 | 62 | 63 | io.vertx 64 | vertx-platform 65 | ${vertx.version} 66 | 67 | 68 | 69 | io.vertx 70 | vertx-hazelcast 71 | ${vertx.version} 72 | provided 73 | 74 | 75 | ch.qos.logback 76 | logback-classic 77 | 1.1.2 78 | 79 | 80 | 81 | 82 | junit 83 | junit 84 | 4.11 85 | test 86 | 87 | 88 | io.vertx 89 | testtools 90 | ${vertx.testtools.version} 91 | test 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | io.vertx 102 | vertx-maven-plugin 103 | ${maven.vertx.plugin.version} 104 | 112 | 113 | 114 | 115 | io.vertx 116 | vertx-platform 117 | ${vertx.version} 118 | 119 | 120 | io.vertx 121 | vertx-core 122 | ${vertx.version} 123 | 124 | 125 | io.vertx 126 | vertx-hazelcast 127 | ${vertx.version} 128 | 129 | 130 | 131 | 132 | PullInDeps 133 | prepare-package 134 | 135 | pullInDeps 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | org.apache.maven.plugins 144 | maven-compiler-plugin 145 | ${maven.compiler.plugin.version} 146 | 147 | 1.7 148 | 1.7 149 | 150 | 151 | 152 | maven-resources-plugin 153 | ${maven.resources.plugin.version} 154 | 155 | 156 | copy-mod-to-target 157 | process-classes 158 | 159 | copy-resources 160 | 161 | 162 | true 163 | ${mods.directory}/${module.name} 164 | 165 | 166 | target/classes 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | org.apache.maven.plugins 175 | maven-dependency-plugin 176 | ${maven.dependency.plugin.version} 177 | 178 | 179 | copy-mod-dependencies-to-target 180 | process-classes 181 | 182 | copy-dependencies 183 | 184 | 185 | ${mods.directory}/${module.name}/lib 186 | runtime 187 | 188 | 189 | 190 | copy-mod-dependencies-to-target-dependencies 191 | process-classes 192 | 193 | copy-dependencies 194 | 195 | 196 | target/dependencies 197 | runtime 198 | 199 | 200 | 201 | 202 | 203 | org.apache.maven.plugins 204 | maven-surefire-plugin 205 | ${maven.surefire.plugin.version} 206 | 207 | 208 | **/unit/*Test*.java 209 | 210 | 211 | 212 | 213 | org.apache.maven.plugins 214 | maven-failsafe-plugin 215 | ${maven.failsafe.plugin.version} 216 | 217 | 218 | 219 | vertx.mods 220 | ${mods.directory} 221 | 222 | 223 | 224 | **/integration/**/*Test* 225 | 226 | 227 | 228 | 229 | 230 | integration-test 231 | verify 232 | 233 | 234 | 235 | 236 | 237 | org.apache.maven.plugins 238 | maven-surefire-report-plugin 239 | ${maven.surefire.report.plugin.version} 240 | 241 | 242 | generate-test-report 243 | test 244 | 245 | report-only 246 | 247 | 248 | 249 | generate-integration-test-report 250 | integration-test 251 | 252 | failsafe-report-only 253 | 254 | 255 | 256 | 257 | 258 | maven-assembly-plugin 259 | 260 | 261 | src/main/assembly/mod.xml 262 | 263 | 264 | 265 | 266 | assemble 267 | package 268 | 269 | single 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | org.apache.maven.plugins 280 | maven-surefire-report-plugin 281 | ${maven.surefire.report.plugin.version} 282 | 283 | 284 | org.apache.maven.plugins 285 | maven-javadoc-plugin 286 | ${maven.javadoc.plugin.version} 287 | 288 | true 289 | 290 | 291 | 292 | 293 | 294 | -------------------------------------------------------------------------------- /samples/vertx/pom.xml.old: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix-samples 7 | 1.0.1-SNAPSHOT 8 | 9 | jimix-samples-vertx 10 | jar 11 | 12 | 13 | 14 | ${project.groupId} 15 | jimix-vertx 16 | ${project.version} 17 | 18 | 19 | ch.qos.logback 20 | logback-classic 21 | 1.1.2 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /samples/vertx/src/main/assembly/mod.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | mod 7 | 8 | zip 9 | 10 | 11 | false 12 | 13 | 14 | 15 | 16 | ${mods.directory}/${module.name} 17 | 18 | ** 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /samples/vertx/src/main/java/org/woelker/jimix/samples/vertx/JimixSampleVertxMain.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.samples.vertx; 2 | 3 | import org.vertx.java.core.Vertx; 4 | import org.vertx.java.core.http.HttpServer; 5 | import org.vertx.java.core.http.RouteMatcher; 6 | import org.vertx.java.core.impl.DefaultVertx; 7 | import org.woelker.jimix.vertx.JimixVertxHandler; 8 | 9 | public class JimixSampleVertxMain { 10 | public static void main(String[] args) throws Exception { 11 | new JimixSampleVertxMain().run(); 12 | } 13 | 14 | private void run() throws Exception { 15 | Vertx vertx = new DefaultVertx(); 16 | HttpServer server = vertx.createHttpServer(); 17 | 18 | RouteMatcher routeMatcher = new RouteMatcher(); 19 | 20 | routeMatcher.allWithRegEx("/jimix(.*)", new JimixVertxHandler()); 21 | 22 | server.requestHandler(routeMatcher).listen(8080, "localhost"); 23 | while (true) { 24 | Thread.sleep(10000); 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /samples/vertx/src/main/resources/mod.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | // Java verticle 4 | "main":"org.woelker.jimix.samples.vertx.JimixSampleVertx", 5 | 6 | // In this example we also provide various other versions of the main verticle, you can uncomment this and 7 | // play around: 8 | 9 | // Java *source* verticle 10 | //"main":"JavaSourcePingVerticle.java", 11 | 12 | // Compiled Groovy verticle - note that they must be prefixed with 'groovy:' to distinguish them from compiled Java verticles 13 | //"main":"groovy:com.mycompany.myproject.GroovyPingVerticle", 14 | 15 | // Groovy *script* verticle 16 | //"main": "GroovyVerticle.groovy", 17 | 18 | // Ruby verticle 19 | //"main":"ping_verticle.rb", 20 | 21 | // JavaScript verticle 22 | //"main": "ping_verticle.js", 23 | 24 | // Python verticle 25 | //"main": "ping_verticle.py", 26 | 27 | 28 | // If your module is going to be registered in the Vert.x module registry you will also need the following 29 | // mandatory fields 30 | "description":"Put description of your module here", 31 | "licenses": ["The Apache Software License Version 2.0"], 32 | "author": "Joe Bloggs", 33 | 34 | // Optional fields 35 | "developers": ["Other Dev 1", "Other Dev 2"], 36 | "keywords": ["gerbils", "cars", "cheese", "chocolate", "underpants", "herring"], 37 | "homepage": "url to your project page", 38 | 39 | "auto-redeploy": true 40 | } -------------------------------------------------------------------------------- /samples/war/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix-samples 7 | 2.2.1-SNAPSHOT 8 | 9 | jimix-samples-war 10 | war 11 | 12 | 13 | 14 | ${project.groupId} 15 | jimix-servlet 16 | ${project.version} 17 | 18 | 19 | 20 | jimix-war 21 | 22 | 23 | org.eclipse.jetty 24 | jetty-maven-plugin 25 | 9.1.3.v20140225 26 | 27 | 28 | org.apache.tomcat.maven 29 | tomcat6-maven-plugin 30 | 2.2 31 | 32 | 33 | org.apache.tomcat.maven 34 | tomcat7-maven-plugin 35 | 2.2 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /samples/war/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | jimix WAR sample 7 | 8 | 9 | jimix 10 | org.woelker.jimix.servlet.JimixServlet 11 | 12 | 13 | 14 | jimix 15 | /jimix/* 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/war/src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hello World!

4 | 5 | 6 | -------------------------------------------------------------------------------- /servlet/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix 7 | 2.2.1-SNAPSHOT 8 | 9 | jimix-servlet 10 | 11 | 12 | 2.41.0 13 | 14 | 15 | 16 | 17 | ${project.groupId} 18 | jimix-core 19 | ${project.version} 20 | 21 | 22 | javax.servlet 23 | servlet-api 24 | 2.4 25 | provided 26 | 27 | 28 | org.eclipse.jetty 29 | jetty-server 30 | 9.1.0.v20131115 31 | test 32 | jar 33 | 34 | 35 | org.eclipse.jetty 36 | jetty-servlet 37 | 9.1.0.v20131115 38 | test 39 | jar 40 | 41 | 42 | junit 43 | junit 44 | 4.11 45 | test 46 | jar 47 | 48 | 49 | com.yammer.metrics 50 | metrics-core 51 | 2.2.0 52 | test 53 | 54 | 55 | ch.qos.logback 56 | logback-classic 57 | 1.1.2 58 | test 59 | 60 | 61 | org.seleniumhq.selenium 62 | selenium-java 63 | ${selenium.version} 64 | test 65 | 66 | 67 | org.seleniumhq.selenium 68 | selenium-firefox-driver 69 | ${selenium.version} 70 | test 71 | 72 | 73 | com.github.detro.ghostdriver 74 | phantomjsdriver 75 | 1.1.0 76 | test 77 | 78 | 79 | -------------------------------------------------------------------------------- /servlet/src/main/java/org/woelker/jimix/servlet/JimixServlet.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.servlet; 2 | 3 | import java.io.IOException; 4 | import javax.servlet.ServletException; 5 | import javax.servlet.http.HttpServlet; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | import org.woelker.jimix.core.JimixRequestHandler; 9 | 10 | public class JimixServlet extends HttpServlet { 11 | 12 | private final JimixRequestHandler jimixRequestHandler; 13 | 14 | public JimixServlet() { 15 | jimixRequestHandler = new JimixRequestHandler(); 16 | } 17 | 18 | @Override 19 | protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 20 | try { 21 | jimixRequestHandler.handle(new ServletHttpRequestWrapper(request, response)); 22 | } catch (Exception ex) { 23 | throw new RuntimeException(ex); 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /servlet/src/main/java/org/woelker/jimix/servlet/ServletHttpRequestWrapper.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.servlet; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | import org.woelker.jimix.core.HttpRequest; 8 | 9 | class ServletHttpRequestWrapper implements HttpRequest { 10 | 11 | private final HttpServletRequest request; 12 | private final HttpServletResponse response; 13 | 14 | public ServletHttpRequestWrapper(HttpServletRequest request, HttpServletResponse response) { 15 | this.request = request; 16 | this.response = response; 17 | } 18 | 19 | @Override 20 | public String getMethod() { 21 | return request.getMethod(); 22 | } 23 | 24 | @Override 25 | public String getPathInfo() { 26 | return request.getPathInfo(); 27 | } 28 | 29 | @Override 30 | public String getRequestURI() { 31 | return request.getRequestURI(); 32 | } 33 | 34 | @Override 35 | public String getQueryString() { 36 | return request.getQueryString(); 37 | } 38 | 39 | @Override 40 | public void setContentType(String contentType) { 41 | response.setContentType(contentType); 42 | } 43 | 44 | @Override 45 | public OutputStream getOutputStream() throws IOException { 46 | return response.getOutputStream(); 47 | } 48 | 49 | @Override 50 | public void sendError(int errorStatus) throws IOException { 51 | response.sendError(errorStatus); 52 | } 53 | 54 | @Override 55 | public void setStatus(int status) { 56 | response.setStatus(status); 57 | } 58 | 59 | @Override 60 | public void sendRedirect(String url) throws IOException { 61 | response.sendRedirect(url); 62 | } 63 | 64 | @Override 65 | public void setAttribute(String key, Object attribute) { 66 | request.setAttribute(key, attribute); 67 | 68 | } 69 | 70 | @Override 71 | public Object getAttribute(String key) { 72 | return request.getAttribute(key); 73 | } 74 | 75 | @Override 76 | public void setHeader(String header, String value) { 77 | response.setHeader(header, value); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /servlet/src/test/java/org/woelker/jimix/servlet/JimixSandbox.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.servlet; 2 | 3 | import com.yammer.metrics.Metrics; 4 | import com.yammer.metrics.core.Gauge; 5 | import com.yammer.metrics.core.MetricName; 6 | import org.eclipse.jetty.server.Server; 7 | import org.eclipse.jetty.servlet.ServletContextHandler; 8 | import org.eclipse.jetty.servlet.ServletHolder; 9 | 10 | public class JimixSandbox { 11 | 12 | public static void main(String[] args) throws Exception { 13 | new JimixSandbox().run(); 14 | } 15 | 16 | private void run() throws Exception { 17 | Metrics.newGauge(JimixSandbox.class, "foo", new Gauge() { 18 | long current = 0; 19 | 20 | @Override 21 | public Long value() { 22 | return current++; 23 | } 24 | }); 25 | Metrics.newGauge(JimixSandbox.class, "foo", "bar", new Gauge() { 26 | long current = 0; 27 | 28 | @Override 29 | public Long value() { 30 | return current++; 31 | } 32 | }); 33 | Metrics.newGauge(JimixSandbox.class, "foo", "double", new Gauge() { 34 | 35 | @Override 36 | public Double value() { 37 | return Double.NaN; 38 | } 39 | }); 40 | final MetricName metricName = new MetricName("fizz", "buzz", "foo"); 41 | Metrics.newGauge(metricName, new Gauge() { 42 | long current = 0; 43 | 44 | @Override 45 | public Long value() { 46 | return current++; 47 | } 48 | }); 49 | 50 | Server server = new Server(8080); 51 | ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); 52 | context.setContextPath("/"); 53 | server.setHandler(context); 54 | 55 | context.addServlet(new ServletHolder(new JimixServlet()), "/jimix/*"); 56 | server.start(); 57 | server.join(); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /servlet/src/test/java/org/woelker/jimix/servlet/JimixServletIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.servlet; 2 | 3 | import com.yammer.metrics.Metrics; 4 | import com.yammer.metrics.core.Gauge; 5 | import com.yammer.metrics.core.MetricName; 6 | import java.util.concurrent.TimeUnit; 7 | import org.eclipse.jetty.server.Server; 8 | import org.eclipse.jetty.servlet.ServletContextHandler; 9 | import org.eclipse.jetty.servlet.ServletHolder; 10 | import org.junit.After; 11 | import org.junit.Before; 12 | import org.junit.Ignore; 13 | import org.junit.Test; 14 | import org.openqa.selenium.By; 15 | import org.openqa.selenium.WebDriver; 16 | import org.openqa.selenium.WebElement; 17 | import org.openqa.selenium.firefox.FirefoxDriver; 18 | import org.openqa.selenium.phantomjs.PhantomJSDriver; 19 | 20 | public class JimixServletIntegrationTest { 21 | 22 | private Server server; 23 | private WebDriver driver; 24 | final MetricName metricName = new MetricName("fizz", "buzz", "foo"); 25 | 26 | @Before 27 | public void before() throws Exception { 28 | final Gauge gauge = Metrics.newGauge(metricName, new Gauge() { 29 | long current = 42; 30 | 31 | @Override 32 | public Long value() { 33 | return current; 34 | } 35 | }); 36 | 37 | server = new Server(8081); 38 | ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); 39 | context.setContextPath("/"); 40 | server.setHandler(context); 41 | 42 | context.addServlet(new ServletHolder(new JimixServlet()), "/jimix/*"); 43 | server.start(); 44 | // driver = new FirefoxDriver(); 45 | driver = new PhantomJSDriver(); 46 | driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS); 47 | } 48 | 49 | @After 50 | public void after() throws Exception { 51 | Metrics.defaultRegistry().removeMetric(metricName); 52 | 53 | server.stop(); 54 | driver.close(); 55 | } 56 | 57 | @Test 58 | @Ignore("Disabled while react UI is being developed") 59 | public void basicTest() throws Exception { 60 | driver.get("http://localhost:8081/jimix/"); 61 | final WebElement element = driver.findElement(By.linkText("buzz - foo")); 62 | element.click(); 63 | driver.findElement(By.xpath("//*[text()='Value']")); 64 | final WebElement value = driver.findElement(By.xpath("//*[text()='42']")); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /ui/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = tab 8 | indent_size = 4 9 | -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /node/ 3 | /dist/ 4 | -------------------------------------------------------------------------------- /ui/assets-assembly.xml: -------------------------------------------------------------------------------- 1 | 4 | assets 5 | 6 | jar 7 | 8 | org/woelker/jimix/ui/assets 9 | 10 | 11 | src 12 | 13 | 14 | *.html 15 | 16 | 17 | 18 | ${project.build.directory}/dist 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ui/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var gutil = require("gulp-util"); 5 | var webpack = require('webpack'); 6 | var WebpackDevServer = require("webpack-dev-server"); 7 | 8 | var webpackConfig = require("./webpack.config.js"); 9 | 10 | gulp.task('default', ['dev']); 11 | 12 | 13 | gulp.task('dev', function () { 14 | var devConfig = Object.create(webpackConfig); 15 | devConfig.plugins.push(new webpack.HotModuleReplacementPlugin()); 16 | devConfig.plugins.push(new webpack.NoErrorsPlugin()); 17 | devConfig.entry.unshift('webpack/hot/only-dev-server'); 18 | devConfig.entry.unshift('webpack-dev-server/client?http://localhost:8081'); 19 | 20 | var compiler = webpack(devConfig); 21 | var server = new WebpackDevServer(compiler, { 22 | // webpack-dev-server options 23 | contentBase: "./src/", 24 | // or: contentBase: "http://localhost/", 25 | 26 | hot: true, 27 | // Enable special support for Hot Module Replacement 28 | // Page is no longer updated, but a "webpackHotUpdate" message is send to the content 29 | // Use "webpack/hot/dev-server" as additional module in your entry point 30 | // Note: this does _not_ add the `HotModuleReplacementPlugin` like the CLI option does. 31 | 32 | // webpack-dev-middleware options 33 | quiet: false, 34 | noInfo: false, 35 | lazy: false, 36 | filename: "bundle.js", 37 | watchDelay: 300, 38 | publicPath: "/", 39 | stats: { 40 | colors: true, 41 | chunks: false, 42 | modules: false 43 | 44 | }, 45 | 46 | // Set this as true if you want to access dev server from arbitrary url. 47 | // This is handy if you are using a html5 router. 48 | // historyApiFallback: false, 49 | 50 | // Set this if you want webpack-dev-server to delegate a single path to an arbitrary server. 51 | // Use "*" to proxy all paths to the specified server. 52 | // This is useful if you want to get rid of 'http://localhost:8080/' in script[src], 53 | // and has many other use cases (see https://github.com/webpack/webpack-dev-server/pull/127 ). 54 | proxy: { 55 | "/api/*": "http://localhost:8080/jimix/" 56 | } 57 | }); 58 | server.listen(8081, "0.0.0.0", function () { 59 | console.log("listening"); 60 | }); 61 | }); 62 | 63 | gulp.task('build', function (callback) { 64 | // modify some webpack config options 65 | var productionConfig = Object.create(webpackConfig); 66 | productionConfig.plugins = productionConfig.plugins.concat( 67 | new webpack.DefinePlugin({ 68 | "process.env": { 69 | // This has effect on the react lib size 70 | "NODE_ENV": JSON.stringify("production") 71 | } 72 | }) 73 | /*, 74 | new webpack.optimize.DedupePlugin(), 75 | new webpack.optimize.UglifyJsPlugin()*/ 76 | ); 77 | productionConfig.output.path = "target/dist"; 78 | productionConfig.output.publicPath = "./"; 79 | productionConfig.debug = false; 80 | productionConfig.devtool = ""; 81 | // run webpack 82 | webpack(productionConfig, function (err, stats) { 83 | if (err) throw new gutil.PluginError("webpack:build", err); 84 | gutil.log("[webpack:build]", stats.toString({ 85 | colors: true 86 | })); 87 | callback(); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jimix-ui", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/manuel-woelker/jimix" 12 | }, 13 | "author": "Manuel Woelker", 14 | "license": "Apache 2", 15 | "bugs": { 16 | "url": "https://github.com/manuel-woelker/jimix/issues" 17 | }, 18 | "homepage": "https://github.com/manuel-woelker/jimix", 19 | "devDependencies": { 20 | "babel-core": "^5.4.7", 21 | "babel-loader": "^5.1.3", 22 | "css-loader": "^0.13.1", 23 | "file-loader": "^0.8.3", 24 | "gulp": "^3.8.11", 25 | "gulp-util": "^3.0.5", 26 | "node-libs-browser": "^0.5.0", 27 | "react-hot-loader": "^1.2.7", 28 | "style-loader": "^0.12.2", 29 | "url-loader": "^0.5.5", 30 | "webpack": "^1.9.7", 31 | "webpack-dev-server": "^1.8.2" 32 | }, 33 | "dependencies": { 34 | "bootstrap": "^3.3.4", 35 | "react": "^0.13.3", 36 | "react-bootstrap": "^0.22.6", 37 | "react-router": "^0.13.3", 38 | "reflux": "^0.2.7", 39 | "superagent": "^1.2.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix 7 | 2.2.1-SNAPSHOT 8 | 9 | jimix-ui 10 | pom 11 | 12 | 1.7 13 | 1.7 14 | 15 | 16 | 17 | 18 | 19 | com.github.eirslett 20 | frontend-maven-plugin 21 | 0.0.23 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | install node and npm 33 | 34 | install-node-and-npm 35 | 36 | 37 | generate-resources 38 | 39 | v0.12.4 40 | 2.12.0 41 | 42 | 43 | 44 | 45 | npm install 46 | 47 | npm 48 | 49 | 50 | 51 | generate-resources 52 | 53 | 54 | 58 | install 59 | 60 | 61 | 62 | 63 | gulp build 64 | 65 | gulp 66 | 67 | 68 | 69 | generate-resources 70 | 71 | 72 | 74 | build 75 | 76 | 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-assembly-plugin 83 | 2.5.5 84 | 85 | 86 | make-assembly 87 | 88 | package 89 | 90 | 91 | single 92 | 93 | 94 | 95 | assets-assembly.xml 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /ui/src/actions/actions.js: -------------------------------------------------------------------------------- 1 | import Reflux from "reflux"; 2 | 3 | export default Reflux.createActions([ 4 | "loadMbean", 5 | "executeOperation", 6 | "setAttributeValue", 7 | "setAutoRefresh" 8 | ]); 9 | -------------------------------------------------------------------------------- /ui/src/application/Application.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import NavigationBar from "./NavigationBar.js"; 3 | import MbeanListContainer from "../mbeans/list/MbeanListContainer.js"; 4 | import MbeanDetailsContainer from "../mbeans/details/MbeanDetailsContainer.js"; 5 | 6 | import {Input, Button, Glyphicon} from "react-bootstrap"; 7 | 8 | import {RouteHandler} from "react-router"; 9 | 10 | export default React.createClass({ 11 | render: function () { 12 | return ( 13 |
14 | 15 |
16 | 17 |
18 |
19 | 20 | ); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /ui/src/application/NavigationBar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Reflux from "reflux"; 3 | 4 | import {Navbar} from "react-bootstrap"; 5 | 6 | import InventoryStore from "../inventory/InventoryStore.js"; 7 | 8 | 9 | export default React.createClass({ 10 | mixins: [Reflux.connect(InventoryStore, "inventory")], 11 | 12 | render() { 13 | let info = null; 14 | let inventory = this.state.inventory; 15 | if (inventory) { 16 | info = - {inventory.mainClass} ({inventory.userName}@{inventory.hostName}); 17 | } 18 | return jimix{info}}> 19 | ; 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /ui/src/application/routes.js: -------------------------------------------------------------------------------- 1 | var Router = require('react-router'); 2 | let {Route, DefaultRoute} = Router; 3 | import React from "react"; 4 | 5 | import Application from "../application/Application.js" 6 | 7 | import MBeansView from "../mbeans/MBeansView.js" 8 | 9 | import MBeanDetailsContainer from "../mbeans/details/MbeanDetailsContainer.js" 10 | 11 | // declare our routes and their hierarchy 12 | var routes = ( 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | 20 | export default routes; 21 | -------------------------------------------------------------------------------- /ui/src/components/Sparkline.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | 4 | function isaNumber(maybeNumber) { 5 | return isFinite(maybeNumber); 6 | } 7 | 8 | export default React.createClass({ 9 | getDefaultProps() { 10 | return { 11 | height: 20, 12 | width: 100 13 | } 14 | }, 15 | 16 | componentDidMount() { 17 | this.redraw(); 18 | }, 19 | 20 | componentDidUpdate() { 21 | this.redraw(); 22 | }, 23 | 24 | redraw() { 25 | // let values = [1, 2, 4, 8, 16]; 26 | // let values = [1, 22, 34, 8, 26, 99, 3, 77, 7]; 27 | // let values = [101, 102, 104, 108, 116]; 28 | // let values = [1000, 2000, 4000, 8000, 16000, undefined, undefined]; 29 | let values = this.props.values; 30 | let canvas = React.findDOMNode(this.refs.canvas); 31 | let context = canvas.getContext("2d", { 32 | antialias: true, 33 | alpha: true 34 | }); 35 | context.resetTransform(); 36 | context.imageSmoothingEnabled = true; 37 | let width = canvas.width; 38 | let height = canvas.height; 39 | context.clearRect(0, 0, width, height); 40 | context.translate(width, height-2); 41 | context.scale(-1, -1); 42 | 43 | let actualValues = values.filter(isFinite); 44 | let minValue = Math.min.apply(0, actualValues); 45 | let maxValue = Math.max.apply(0, actualValues); 46 | let scale = Math.max(0.0000001, maxValue - minValue) / (height-4); 47 | let stepSize = width / (values.length - 1); 48 | context.lineWidth = 1; 49 | context.beginPath(); 50 | context.moveTo(0, (values[0] - minValue) / scale); 51 | for (var i = 1; i < values.length; i++) { 52 | if(!values[i]) { 53 | break; 54 | } 55 | context.lineTo(stepSize * i, (values[i] - minValue) / scale); 56 | } 57 | context.stroke(); 58 | 59 | }, 60 | 61 | 62 | render() { 63 | return
64 | 65 |
; 66 | } 67 | }); 68 | -------------------------------------------------------------------------------- /ui/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jimix 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /ui/src/inventory/InventoryStore.js: -------------------------------------------------------------------------------- 1 | import request from 'superagent'; 2 | import Reflux from 'reflux'; 3 | 4 | function stripQuotes(str) { 5 | if (str.length === 0) { 6 | return str; 7 | } 8 | if (str.charAt(0) === "\"" && str.charAt(str.length - 1)) { 9 | return str.substring(1, str.length - 1); 10 | } 11 | return str; 12 | } 13 | 14 | 15 | function byName(a, b) { 16 | if (a.name === b.name) { 17 | return 0; 18 | } 19 | return a.name < b.name ? -1 : 1; 20 | }; 21 | 22 | 23 | const re = /([^:]+):(.+)/; 24 | 25 | export default Reflux.createStore({ 26 | 27 | // Initial setup 28 | init: function() { 29 | this.state = {}; 30 | setTimeout(() => 31 | request 32 | .get('api/inventory') 33 | .end((err, res) => { 34 | let inventory = res.body; 35 | let domainMap = {}; 36 | inventory.mbeans.forEach(function(mbean) { 37 | var match = re.exec(mbean.objectName); 38 | var domain = match[1]; 39 | var name = match[2]; 40 | mbean.name = name; 41 | var namePart = null; 42 | var typePart = null; 43 | var scopePart = null; 44 | var additionalKeys = 0; 45 | name.split(",").forEach(function(item) { 46 | var parts = item.split("="); 47 | var key = parts[0].toLowerCase(); 48 | if (key === "type") { 49 | typePart = parts[1]; 50 | return; 51 | } 52 | if (key === "name") { 53 | namePart = parts[1]; 54 | return; 55 | } 56 | if (key === "scope") { 57 | scopePart = parts[1]; 58 | return; 59 | } 60 | additionalKeys++; 61 | }); 62 | if (additionalKeys <= 0) { 63 | if (typePart) { 64 | name = stripQuotes(typePart) 65 | } 66 | if (namePart) { 67 | if(typePart) { 68 | name += " - " + stripQuotes(namePart); 69 | } else { 70 | name = stripQuotes(namePart); 71 | } 72 | } 73 | if (scopePart) { 74 | name += " (" + stripQuotes(scopePart) + ")"; 75 | } 76 | mbean.name = name; 77 | } 78 | domainMap[domain] = domainMap[domain] || {name: stripQuotes(domain), mbeans: []}; 79 | domainMap[domain].mbeans.push(mbean); 80 | }); 81 | var domains = []; 82 | for (var domain in domainMap) { 83 | domainMap[domain].expanded = true; 84 | domainMap[domain].mbeans.sort(byName); 85 | domains.push(domainMap[domain]); 86 | } 87 | domains.sort(byName); 88 | inventory.domains = domains; 89 | this.state.inventory = inventory; 90 | this.trigger(inventory); 91 | }), 10); 92 | 93 | } 94 | 95 | 96 | }); 97 | -------------------------------------------------------------------------------- /ui/src/main.js: -------------------------------------------------------------------------------- 1 | import Application from "./application/Application.js"; 2 | import React from "react"; 3 | import Router from "react-router"; 4 | import routes from "./application/routes.js"; 5 | 6 | require("bootstrap/dist/css/bootstrap.css"); 7 | require("./style/style.css"); 8 | 9 | 10 | window.onload = function() { 11 | Router.run(routes, Router.HashLocation, (Root) => { 12 | React.render(, document.getElementById('application')); 13 | }); 14 | /* 15 | React.render( 16 | , 17 | document.getElementById('application') 18 | ); 19 | */ 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /ui/src/mbeans/MBeansView.js: -------------------------------------------------------------------------------- 1 | 2 | import React from "react"; 3 | import MbeanListContainer from "./list/MbeanListContainer.js" 4 | import MbeanDetailsContainer from "./list/MbeanListContainer.js" 5 | 6 | import {RouteHandler, State} from "react-router"; 7 | 8 | export default React.createClass({ 9 | mixins: [State], 10 | 11 | render() { 12 | var key = this.getPath(); 13 | return
14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 |
22 |
; 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /ui/src/mbeans/MbeanStore.js: -------------------------------------------------------------------------------- 1 | import request from 'superagent'; 2 | import Reflux from 'reflux'; 3 | 4 | import actions from "../actions/actions.js"; 5 | 6 | export default Reflux.createStore({ 7 | listenables: actions, 8 | // Initial setup 9 | init: function () { 10 | this.previousValues = {}; 11 | this.historicValueMap = {}; 12 | }, 13 | 14 | onLoadMbean(objectName, autoRefresh) { 15 | this.cancelAutoRefreshTimer(); 16 | this.previousValues = {}; 17 | this.historicValueMap = {}; 18 | this.currentObjectName = objectName; 19 | this.loadMbean(); 20 | this.onSetAutoRefresh(autoRefresh); 21 | }, 22 | 23 | 24 | loadMbean() { 25 | request 26 | .get('api/mbeans/' + encodeURIComponent(this.currentObjectName)).end((err, res) => { 27 | let mbean = this.mbean = JSON.parse(res.text); 28 | // Compute deltas 29 | let previousValues = this.previousValues; 30 | let historicValueMap = this.historicValueMap; 31 | let values = {}; 32 | mbean.attributes.forEach(attribute => { 33 | let value = attribute.value; 34 | values[attribute.name] = value; 35 | let previousValue = previousValues[attribute.name]; 36 | if (Number.isFinite(value)) { 37 | if (previousValue !== undefined) { 38 | attribute.delta = attribute.value - previousValue; 39 | } 40 | let historicValues = historicValueMap[attribute.name]; 41 | if(typeof historicValues === "undefined") { 42 | historicValues = new Array(10); 43 | historicValues[0] = attribute.value; 44 | historicValueMap[attribute.name] = historicValues; 45 | } else { 46 | historicValues.unshift(attribute.value); 47 | historicValues.length = historicValues.length - 1; 48 | attribute.historicValues = historicValues; 49 | } 50 | } 51 | if(typeof previousValue !== "undefined" && previousValue !== attribute.value) { 52 | attribute.changed = true; 53 | } 54 | 55 | }); 56 | this.previousValues = values; 57 | this.trigger(mbean); 58 | }); 59 | }, 60 | 61 | onExecuteOperation(invocation) { 62 | let operation = invocation.operation; 63 | operation.invoking = true; 64 | operation.result = null; 65 | this.trigger(this.mbean); 66 | request 67 | .post('api/mbeans/' + encodeURIComponent(invocation.objectName) + "/" + operation.name) 68 | .query(invocation.parameters.map((parameter) => "argument=" + encodeURIComponent(parameter)).join("&")) 69 | .end((err, res) => { 70 | operation.invoking = false; 71 | if (err) { 72 | operation.success = false; 73 | operation.result = "Unknown Error"; 74 | try { 75 | operation.result = err.response.statusText; 76 | } finally { 77 | } 78 | try { 79 | let response = JSON.parse(res.text); 80 | if (response && response.message) { 81 | operation.result = response.message; 82 | } 83 | } finally { 84 | } 85 | this.trigger(this.mbean); 86 | return; 87 | } 88 | operation.success = true; 89 | let response = JSON.parse(res.text); 90 | operation.result = response.result || "OK"; 91 | this.trigger(this.mbean); 92 | }); 93 | }, 94 | 95 | onSetAttributeValue(parameters) { 96 | let attribute = parameters.attribute; 97 | request 98 | .put('api/mbeans/' + parameters.objectName + "/" + attribute.name) 99 | .query("value=" + encodeURIComponent(parameters.value)) 100 | .end((err, res) => { 101 | if (err) { 102 | console.log(err); 103 | return; 104 | } 105 | attribute.value = parameters.value; 106 | this.trigger(this.mbean); 107 | }); 108 | }, 109 | 110 | cancelAutoRefreshTimer() { 111 | if (this.TimerId) { 112 | clearInterval(this.TimerId); 113 | this.TimerId = null; 114 | } 115 | }, 116 | 117 | onSetAutoRefresh(autoRefresh) { 118 | this.autoRefresh = autoRefresh; 119 | this.cancelAutoRefreshTimer(); 120 | if (autoRefresh) { 121 | this.TimerId = setInterval(() => this.loadMbean(), autoRefresh * 1000); 122 | } 123 | } 124 | 125 | 126 | }); 127 | -------------------------------------------------------------------------------- /ui/src/mbeans/details/AttributeValue.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Input, Button, Glyphicon, Table} from "react-bootstrap"; 3 | 4 | import actions from "../../actions/actions.js"; 5 | 6 | import Sparkline from "../../components/Sparkline.js"; 7 | 8 | function formatNumber(number) { 9 | let result = number; 10 | if(number % 1 !== 0) { 11 | result = Number(number).toFixed(3); 12 | } 13 | return result; 14 | } 15 | 16 | 17 | export default React.createClass({ 18 | getInitialState() { 19 | return { 20 | editValue: null, 21 | editing: false 22 | } 23 | }, 24 | 25 | startEditing() { 26 | setTimeout(() => { 27 | let inputNode = React.findDOMNode(this.refs.input); 28 | inputNode.focus(); 29 | }); 30 | this.setState({editing: true, editValue: this.props.attribute.value}); 31 | }, 32 | 33 | endEditing(event) { 34 | if(!this.state.editing) { 35 | // editing was cancelled 36 | return; 37 | } 38 | actions.setAttributeValue({objectName: this.props.objectName, attribute: this.props.attribute, value: this.state.editValue}); 39 | this.setState({editing: false}); 40 | }, 41 | 42 | onChangeInput() { 43 | let inputNode = React.findDOMNode(this.refs.input); 44 | this.setState({editValue: inputNode.value}); 45 | }, 46 | 47 | onInputKeyPress(event) { 48 | if(event.keyCode === 13) { 49 | this.endEditing(); 50 | } else if(event.keyCode === 27) { 51 | this.setState({editing: false}); 52 | } 53 | 54 | }, 55 | 56 | render() { 57 | let attribute = this.props.attribute; 58 | let glyphElement = null; 59 | let valueElement; 60 | let deltaElement = null; 61 | let inputStyle={ 62 | display: "none" 63 | }; 64 | if (this.state.editing) { 65 | inputStyle.display = "inline-block"; 66 | glyphElement = 67 | valueElement = ""; 68 | } else { 69 | valueElement = attribute.value; 70 | let delta = attribute.delta; 71 | if(delta !== undefined) { 72 | delta = formatNumber(delta); 73 | if(delta > 0) { 74 | deltaElement = (+{delta}); 75 | } else if(delta < 0) { 76 | deltaElement = ({delta}); 77 | } else { 78 | deltaElement = (=); 79 | } 80 | valueElement = {deltaElement} {formatNumber(valueElement)}; 81 | } 82 | if(typeof(attribute.value) === "boolean"){ 83 | valueElement = attribute.value ? "true" : "false"; 84 | } 85 | if (attribute.writable) { 86 | glyphElement = 87 | } 88 | } 89 | 90 | return
91 | {attribute.historicValues?:null} 92 |
93 | {valueElement} 94 | 96 |
97 |
98 | {glyphElement} 99 |
100 |
; 101 | } 102 | }); 103 | -------------------------------------------------------------------------------- /ui/src/mbeans/details/MbeanDetailsContainer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Router from "react-router"; 3 | import Reflux from "reflux"; 4 | import {Input, Button, ButtonGroup, Glyphicon, Table} from "react-bootstrap"; 5 | 6 | import MbeanStore from "../MbeanStore.js"; 7 | import actions from "../../actions/actions.js"; 8 | 9 | import AttributeValue from "./AttributeValue.js"; 10 | 11 | var requestData = function (objectName, autoRefresh) { 12 | actions.loadMbean(objectName, autoRefresh); 13 | }; 14 | 15 | function filterByName(searchText) { 16 | return (item) => item.name.toLowerCase().indexOf(searchText) > -1; 17 | } 18 | 19 | export default React.createClass({ 20 | mixins: [Reflux.connect(MbeanStore, "mbean"), Router.Navigation, Router.State], 21 | 22 | getInitialState() { 23 | this.inputComponents = {}; 24 | return { 25 | searchText: "" 26 | } 27 | }, 28 | 29 | onChangeSearchText() { 30 | this.setState({searchText: this.refs.searchText.getValue()}) 31 | }, 32 | 33 | onClearSearchText() { 34 | this.setState({searchText: ""}) 35 | }, 36 | 37 | 38 | componentWillMount() { 39 | requestData(this.props.params.objectName, Number(this.props.query.autoRefresh)); 40 | }, 41 | 42 | componentWillReceiveProps(nextProps) { 43 | if (nextProps.params.objectName !== this.props.params.objectName || nextProps.query !== this.props.query) { 44 | requestData(nextProps.params.objectName, Number(nextProps.query.autoRefresh)); 45 | } 46 | }, 47 | 48 | onExecuteOperation(operation) { 49 | let parameters = this.inputComponents[operation.name].map((inputComponent) => { 50 | return React.findDOMNode(inputComponent).value; 51 | }); 52 | actions.executeOperation({ 53 | objectName: this.props.params.objectName, 54 | operation, 55 | parameters 56 | }) 57 | }, 58 | 59 | setAutoRefresh(autoRefresh) { 60 | let routes = this.getRoutes(); 61 | let route = routes[routes.length -1]; 62 | let query = this.getQuery(); 63 | query.autoRefresh = autoRefresh; 64 | this.transitionTo(route.name, this.props.params, query); 65 | actions.setAutoRefresh(autoRefresh); 66 | }, 67 | 68 | 69 | render: function () { 70 | function renderValue(attribute) { 71 | return {attribute.value}; 72 | } 73 | 74 | if (!this.state.mbean) { 75 | return null; 76 | } 77 | let attributes = this.state.mbean.attributes; 78 | let operations = this.state.mbean.operations; 79 | let autoRefresh = this.props.query.autoRefresh; 80 | return ( 81 |
82 |
83 |
84 | } onChange={this.onChangeSearchText} value={this.state.searchText} placeholder="Search..."/> 85 |
86 |
87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 |
95 |
96 | 97 |
98 |

Attributes

99 |
100 |
101 | 102 | 103 | 104 | 105 | 106 | 107 | {attributes.filter(filterByName(this.state.searchText)).map((attribute) => 108 | 109 | 110 | 111 | )} 112 | 113 |
{attribute.name}
114 |
115 | 116 |
117 |

Operations

118 |
119 |
120 | 121 | 122 | 123 | 124 | 125 | 126 | {operations.filter(filterByName(this.state.searchText)).map((operation) => { 127 | let inputComponents = this.inputComponents[operation.name] = this.inputComponents[operation.name] || []; 128 | return 129 | 133 | 151 | 152 | } 153 | )} 154 | 155 |
130 | 132 | 134 | {operation.signature.length < 1 ? null :
135 | 136 | 137 | {operation.signature.map((parameter, i) => 138 | 142 | 143 | )} 144 | 145 |
139 | {inputComponents[i] = component}} 140 | type="text" className="form-control input-sm"> 141 |   ({parameter.type})
146 |
147 |
} 148 | {operation.result}{operation.invoking ? "Invoking..." : null} 150 |
156 |
157 |
158 | 159 | ); 160 | } 161 | }); 162 | -------------------------------------------------------------------------------- /ui/src/mbeans/list/MbeanListContainer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Router from "react-router"; 3 | 4 | import request from 'superagent'; 5 | import Reflux from 'reflux'; 6 | import InventoryStore from '../../inventory/InventoryStore.js'; 7 | 8 | import {Input, Button, Glyphicon, Panel, Well, ListGroup, ListGroupItem, Table} from "react-bootstrap"; 9 | import {Link} from "react-router"; 10 | 11 | export default React.createClass({ 12 | mixins: [Reflux.connect(InventoryStore, "inventory"), Router.State], 13 | 14 | getInitialState(){ 15 | return { 16 | searchText: "" 17 | } 18 | }, 19 | 20 | onChangeSearchText() { 21 | this.setState({searchText: this.refs.searchText.getValue()}) 22 | }, 23 | 24 | onClearSearchText() { 25 | this.setState({searchText: ""}) 26 | }, 27 | 28 | render: function () { 29 | let inventory = this.state.inventory; 30 | if (!inventory) { 31 | return null; 32 | } 33 | let query = this.getQuery(); 34 | return ( 35 |
36 |
37 |
38 | } onChange={this.onChangeSearchText} value={this.state.searchText} placeholder="Search..."/> 39 |
40 |
41 |
42 | {inventory.domains.map(domain => { 43 | return {domain.name}}> 45 | 46 | 47 | {domain.mbeans.filter(mbean => mbean.name.toLowerCase().indexOf(this.state.searchText) > -1).map(mbean => { 48 | return 49 | 51 | ; 52 | })} 53 | 54 |
{mbean.name}
55 |
; 56 | })} 57 |
58 | 59 |
60 | ); 61 | } 62 | }); 63 | -------------------------------------------------------------------------------- /ui/src/style/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | margin: 0 5 | } 6 | 7 | .button-icon { 8 | cursor: pointer; 9 | } 10 | 11 | #application { 12 | height: 100%; 13 | margin: 0 14 | } 15 | .mbean-view { 16 | height: 100%; 17 | } 18 | 19 | .domain { 20 | text-overflow: ellipsis; 21 | overflow: hidden; 22 | cursor: pointer; 23 | margin-bottom: 10px; 24 | font-weight: bold; 25 | border: 1px solid #ccc; 26 | background-color: #fafafa; 27 | padding: 2px; 28 | padding-left: 10px; 29 | border-radius: 4px; 30 | } 31 | 32 | .mbean { 33 | font-weight: normal; 34 | text-overflow: ellipsis; 35 | overflow: hidden; 36 | white-space: normal; 37 | width: 100%; 38 | text-align: left; 39 | padding-left: 20px; 40 | position: relative; 41 | } 42 | 43 | .mbean .active { 44 | color: #045; 45 | font-style: italic; 46 | } 47 | 48 | 49 | .active { 50 | font-weight: bold; 51 | } 52 | 53 | .mbean:before { 54 | content: "⁍"; 55 | left: 10px; 56 | position: absolute; 57 | } 58 | 59 | .mbean-value { 60 | display: inline-block; 61 | text-overflow: ellipsis; 62 | overflow: hidden; 63 | white-space: normal; 64 | max-width: 600px; 65 | } 66 | 67 | .table-condensed > thead > tr > th, 68 | .table-condensed > tbody > tr > th, 69 | .table-condensed > tfoot > tr > th, 70 | .table-condensed > thead > tr > td, 71 | .table-condensed > tbody > tr > td, 72 | .table-condensed > tfoot > tr > td { 73 | padding-top: 2px; 74 | padding-bottom: 2px; 75 | } 76 | 77 | .table > thead > tr > th, 78 | .table > tbody > tr > th, 79 | .table > tfoot > tr > th, 80 | .table > thead > tr > td, 81 | .table > tbody > tr > td, 82 | .table > tfoot > tr > td { 83 | vertical-align: middle; 84 | } 85 | 86 | 87 | .operation-success { 88 | color: #00A000; 89 | } 90 | 91 | .operation-error { 92 | color: #A00000; 93 | } 94 | 95 | .complex-operation { 96 | cursor: pointer; 97 | } 98 | 99 | .table-striped > tbody > tr.changed:nth-of-type(even) { 100 | background-color: #def; 101 | } 102 | 103 | .table-striped > tbody > tr.changed:nth-of-type(odd) { 104 | background-color: #cde; 105 | } 106 | -------------------------------------------------------------------------------- /ui/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require("webpack"); 3 | 4 | module.exports = { 5 | // This is the main file that should include all other JS files 6 | entry: [ 7 | // 'webpack-dev-server/client?http://localhost:8081', // WebpackDevServer host and port 8 | // 'webpack/hot/only-dev-server', 9 | "./src/main.js"], 10 | target: "web", 11 | debug: true, 12 | devtool: 'eval', 13 | output: { 14 | path: __dirname + "/dist/assets", 15 | publicPath: "/", 16 | // If you want to generate a filename with a hash of the content (for cache-busting) 17 | // filename: "main-[hash].js", 18 | filename: "bundle.js", 19 | chunkFilename: "[chunkhash].js" 20 | }, 21 | resolve: { 22 | modulesDirectories: ['bower_components', 'node_modules'] 23 | }, 24 | module: { 25 | loaders: [ 26 | {test: /\.js$/, loaders: ['react-hot',"babel"], include: path.join(__dirname, 'src')}, 27 | {test: /\.css$/, loader: "style!css"}, 28 | { test: /\.woff$/, loader: "file-loader" }, 29 | { test: /\.woff2$/, loader: "file-loader" }, 30 | { test: /\.ttf$/, loader: "file-loader" }, 31 | { test: /\.eot$/, loader: "file-loader" }, 32 | { test: /\.svg$/, loader: "file-loader" } 33 | ] 34 | }, 35 | plugins: [ 36 | // new webpack.HotModuleReplacementPlugin(), 37 | // new webpack.NoErrorsPlugin() 38 | ] 39 | }; 40 | -------------------------------------------------------------------------------- /vertx/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix 7 | 2.2.1-SNAPSHOT 8 | 9 | jimix-vertx 10 | 11 | 12 | 2.1RC2 13 | 14 | 15 | 16 | 17 | ${project.groupId} 18 | jimix-core 19 | ${project.version} 20 | 21 | 22 | 23 | io.vertx 24 | vertx-core 25 | ${vertx.version} 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /vertx/src/main/java/org/woelker/jimix/vertx/JimixVertxHandler.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.vertx; 2 | 3 | import org.vertx.java.core.Handler; 4 | import org.vertx.java.core.http.HttpServerRequest; 5 | import org.woelker.jimix.core.JimixRequestHandler; 6 | import org.woelker.jimix.core.RequestHandler; 7 | 8 | public class JimixVertxHandler implements Handler { 9 | 10 | RequestHandler requestHandler = new JimixRequestHandler(); 11 | 12 | @Override 13 | public void handle(HttpServerRequest request) { 14 | try { 15 | requestHandler.handle(new VertxHttpRequestWrapper(request)); 16 | request.response().end(); 17 | } catch (Exception e) { 18 | throw new RuntimeException(e); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vertx/src/main/java/org/woelker/jimix/vertx/VertxHttpRequestWrapper.java: -------------------------------------------------------------------------------- 1 | package org.woelker.jimix.vertx; 2 | 3 | import org.vertx.java.core.buffer.Buffer; 4 | import org.vertx.java.core.http.HttpServerRequest; 5 | import org.vertx.java.core.http.HttpServerResponse; 6 | import org.woelker.jimix.core.HttpRequest; 7 | 8 | import java.io.IOException; 9 | import java.io.OutputStream; 10 | import java.net.URI; 11 | import java.net.URISyntaxException; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | class VertxHttpRequestWrapper implements HttpRequest { 16 | 17 | private final HttpServerRequest request; 18 | private final HttpServerResponse response; 19 | private Map attributes = new HashMap(); 20 | 21 | public VertxHttpRequestWrapper(HttpServerRequest request) { 22 | this.request = request; 23 | this.response = request.response(); 24 | } 25 | 26 | @Override 27 | public String getMethod() { 28 | return request.method(); 29 | } 30 | 31 | @Override 32 | public String getPathInfo() { 33 | try { 34 | return urlDecode(request.params().get("param0")); 35 | } catch (URISyntaxException e) { 36 | return request.params().get("param0"); 37 | } 38 | } 39 | 40 | @Override 41 | public String getQueryString() { 42 | return request.query(); 43 | } 44 | 45 | 46 | 47 | private String urlDecode(String url) throws URISyntaxException { 48 | return new URI(url).getPath(); 49 | } 50 | 51 | @Override 52 | public String getRequestURI() { 53 | return request.uri(); 54 | } 55 | 56 | @Override 57 | public void setContentType(String contentType) { 58 | request.response().putHeader("Content-Type", contentType); 59 | } 60 | 61 | @Override 62 | public OutputStream getOutputStream() throws IOException { 63 | response.setChunked(true); 64 | return new OutputStream() { 65 | @Override 66 | public void write(int b) throws IOException { 67 | Buffer buffer = new Buffer(1); 68 | buffer.appendByte((byte) b); 69 | response.write(buffer); 70 | } 71 | 72 | @Override 73 | public void write(byte[] b, int off, int len) throws IOException { 74 | Buffer buffer = new Buffer(len); 75 | buffer.appendBytes(b, off, len); 76 | response.write(buffer); 77 | } 78 | }; 79 | } 80 | 81 | @Override 82 | public void sendError(int errorStatus) throws IOException { 83 | response.setStatusCode(errorStatus); 84 | } 85 | 86 | @Override 87 | public void setStatus(int status) throws IOException { 88 | response.setStatusCode(status); 89 | } 90 | 91 | @Override 92 | public void sendRedirect(String url) throws IOException { 93 | response.putHeader("Location", url); 94 | response.setStatusCode(301); 95 | } 96 | 97 | @Override 98 | public void setAttribute(String key, Object attribute) { 99 | attributes.put(key, attribute); 100 | 101 | } 102 | 103 | @Override 104 | public Object getAttribute(String key) { 105 | return attributes.get(key); 106 | } 107 | 108 | @Override 109 | public void setHeader(String header, String value) { 110 | response.putHeader(header, value); 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /war/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.woelker.jimix 6 | jimix 7 | 2.2.1-SNAPSHOT 8 | 9 | org.woelker.jimix.war 10 | jimix 11 | war 12 | 13 | 14 | 15 | org.woelker.jimix 16 | jimix-servlet 17 | ${project.version} 18 | 19 | 20 | 21 | jimix-war 22 | 23 | 24 | org.eclipse.jetty 25 | jetty-maven-plugin 26 | 9.1.3.v20140225 27 | 28 | 29 | /jimix 30 | 31 | 32 | 33 | 34 | org.apache.tomcat.maven 35 | tomcat6-maven-plugin 36 | 2.2 37 | 38 | 39 | org.apache.tomcat.maven 40 | tomcat7-maven-plugin 41 | 2.2 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /war/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | jimix - A modern JMX web console 6 | 7 | 8 | jimix 9 | org.woelker.jimix.servlet.JimixServlet 10 | 11 | 12 | 13 | jimix 14 | /* 15 | 16 | 17 | --------------------------------------------------------------------------------