├── .classpath ├── .factorypath ├── .gitignore ├── .project ├── .settings ├── .jsdtscope ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.apt.core.prefs ├── org.eclipse.jdt.core.prefs ├── org.eclipse.m2e.core.prefs ├── org.eclipse.wst.common.component ├── org.eclipse.wst.common.project.facet.core.prefs.xml ├── org.eclipse.wst.common.project.facet.core.xml ├── org.eclipse.wst.jsdt.ui.superType.container ├── org.eclipse.wst.jsdt.ui.superType.name └── org.eclipse.wst.validation.prefs ├── LICENSE ├── README.md ├── manifest.yml ├── pom.xml ├── src └── main │ ├── java │ └── example │ │ └── nosql │ │ ├── AttachServlet.java │ │ ├── CloudantClientMgr.java │ │ └── ResourceServlet.java │ ├── webapp │ ├── META-INF │ │ └── MANIFEST.MF │ ├── WEB-INF │ │ └── web.xml │ ├── images │ │ ├── add.png │ │ ├── detail.png │ │ ├── info.png │ │ ├── loading.gif │ │ ├── newapp-icon.png │ │ ├── remove.png │ │ └── sprite.png │ ├── index.html │ ├── index.js │ ├── style.css │ └── util.js │ └── wlp │ └── server.xml └── target └── JavaCloudantApp.war /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.factorypath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/* 2 | !target/JavaCloudantApp.war 3 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | JavaCloudantApp 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.wst.jsdt.core.javascriptValidator 10 | 11 | 12 | 13 | 14 | org.eclipse.jdt.core.javabuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.wst.common.project.facet.core.builder 20 | 21 | 22 | 23 | 24 | org.eclipse.wst.validation.validationbuilder 25 | 26 | 27 | 28 | 29 | org.eclipse.m2e.core.maven2Builder 30 | 31 | 32 | 33 | 34 | 35 | org.eclipse.m2e.core.maven2Nature 36 | org.eclipse.jem.workbench.JavaEMFNature 37 | org.eclipse.wst.common.modulecore.ModuleCoreNature 38 | org.eclipse.wst.common.project.facet.core.nature 39 | org.eclipse.jdt.core.javanature 40 | org.eclipse.wst.jsdt.core.jsNature 41 | 42 | 43 | -------------------------------------------------------------------------------- /.settings/.jsdtscope: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding/=UTF-8 4 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.apt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.apt.aptEnabled=true 3 | org.eclipse.jdt.apt.reconcileEnabled=true 4 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 4 | org.eclipse.jdt.core.compiler.compliance=1.7 5 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 6 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 7 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 8 | org.eclipse.jdt.core.compiler.processAnnotations=enabled 9 | org.eclipse.jdt.core.compiler.source=1.7 10 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.component: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.project.facet.core.prefs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.project.facet.core.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.jsdt.ui.superType.container: -------------------------------------------------------------------------------- 1 | org.eclipse.wst.jsdt.launching.baseBrowserLibrary -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.jsdt.ui.superType.name: -------------------------------------------------------------------------------- 1 | Window -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.validation.prefs: -------------------------------------------------------------------------------- 1 | disabled=06target 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java Cloudant Sample 2 | 3 | This application demonstrates how to use the Bluemix Cloudant NoSQL Database service. It helps users organize their favorite files. The UI talks to a RESTful JAX-RS CRUD backend API. 4 | 5 | [![Deploy to Bluemix](https://bluemix.net/deploy/button.png)](https://bluemix.net/deploy?repository=https://github.com/IBM-Bluemix/java-cloudant) 6 | 7 | ## Building and running the application using the command-line 8 | 9 | 1. Open the [Bluemix dashboard](https://bluemix.net), create/select your Cloudant service -> Service Credentials -> View Credentials 10 | - Copy the credentials to src/main/java/example/nosql/CloudantClientMgr.java 11 | 12 | ``` 13 | user = "REPLACE_WITH_CLOUDANT_USERNAME"; 14 | password = "REPLACE_WITH_CLOUDANT_PASSWORD"; 15 | ``` 16 | 17 | 2. Execute full Maven build to create the `target/JavaCloudantApp.war` file: 18 | ```bash 19 | $ mvn clean install 20 | ``` 21 | 22 | 3. Download and start a local Liberty server with the application: 23 | ```bash 24 | $ mvn liberty:run-server 25 | ``` 26 | 27 | Once the server is running, the application will be available under [http://localhost:9080/JavaCloudantApp](http://localhost:9080/JavaCloudantApp). 28 | 29 | 4. Update the application details in the `manifest.yml` file. Change the `name` field to your Bluemix Application Name and change the `services` field to your Cloudant service instance name. Connect and Login to Bluemix using the following commands: 30 | ```bash 31 | $ cf api https://api.ng.bluemix.net 32 | $ cf login -u username -o org_name -s space_name 33 | ``` 34 | (You can find the `org_name`, `space_name` on the Bluemix dashboard and replace `username` with the `mail-id` associated with the account) 35 | 36 | Use the following command to push the application to Bluemix: 37 | ```bash 38 | $ cf push 39 | ``` 40 | 41 | ## Developing and Deploying using Eclipse 42 | 43 | IBM® Eclipse Tools for Bluemix® provides plug-ins that can be installed into an existing Eclipse environment to assist in integrating the developer's integrated development environment (IDE) with Bluemix. 44 | 45 | 1. Download and install [IBM Eclipse Tools for Bluemix](https://developer.ibm.com/wasdev/downloads/#asset/tools-IBM_Eclipse_Tools_for_Bluemix). 46 | 47 | 2. Import this sample into Eclipse using `File` -> `Import` -> `Maven` -> `Existing Maven Projects` option. 48 | 49 | 3. Create a Liberty server definition: 50 | - In the `Servers` view right-click -> `New` -> `Server` 51 | - Select `IBM` -> `WebSphere Application Server Liberty` 52 | - Choose `Install from an archive or a repository` 53 | - Enter a destination path (/Users/username/liberty) 54 | - Choose `WAS Liberty with Java EE 7 Web Profile` 55 | - Continue the wizard with default options to Finish 56 | 57 | 4. Open the [Bluemix dashbaord](bluemix.net) create/select your Cloudant service -> Service Credentials -> View Credentials 58 | - Copy the credentials to src/example/nosql/CloudantClientMgr.java 59 | 60 | ``` 61 | user = "REPLACE_WITH_CLOUDANT_USERNAME"; 62 | password = "REPLACE_WITH_CLOUDANT_PASSWORD"; 63 | ``` 64 | 65 | 4. Run your application locally on Liberty: 66 | - Right click on the `JavaCloudantApp` sample and select `Run As` -> `Run on Server` option 67 | - Find and select the localhost Liberty server and press `Finish` 68 | - In a few seconds, your application should be running at http://localhost:9080/JavaHelloWorldApp/ 69 | 70 | 5. Create a Bluemix server definition: 71 | - In the `Servers` view, right-click -> `New` -> `Server` 72 | - Select `IBM` -> `IBM Bluemix` and follow the steps in the wizard. 73 | - Enter your credentials and click `Next` 74 | - Select your `org` and `space` and click `Finish` 75 | 76 | 6. Run your application on Bluemix: 77 | - Right click on the `JavaCloudantApp` sample and select `Run As` -> `Run on Server` option 78 | - Find and select the `IBM Bluemix` and press `Finish` 79 | - A wizard will guide you with the deployment options. 80 | - Select your Cloudant service on the Services step 81 | - In a few minutes, your application should be running at the URL you chose. 82 | 83 | Now you have your code running locally and on the cloud! 84 | 85 | [Liberty Maven Plug-in]: https://github.com/WASdev/ci.maven 86 | -------------------------------------------------------------------------------- /manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | declared-services: 3 | sample-java-cloudant-cloudantNoSQLDB: 4 | label: cloudantNoSQLDB 5 | plan: Lite 6 | applications: 7 | - name: sample-java-cloudant 8 | random-route: true 9 | memory: 512M 10 | path: target/JavaCloudantApp.war 11 | services: 12 | - sample-java-cloudant-cloudantNoSQLDB 13 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | net.wasdev.maven.parent 8 | java7-parent 9 | 1.4 10 | 11 | 12 | 13 | net.wasdev.wlp.sample 14 | JavaCloudantApp 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | Liberty Profile Sample - Servlet 19 | https://wasdev.github.io 20 | 21 | 22 | 23 | The Apache Software License, Version 2.0 24 | https://raw.github.com/WASdev/sample.servlet/master/LICENSE 25 | repo 26 | 27 | 28 | 29 | 30 | 31 | liberty-starter-maven-repo 32 | liberty-starter-maven-repo 33 | http://liberty-starter.wasdev.developer.ibm.com/start/api/v1/repo 34 | 35 | 36 | 37 | 38 | 39 | 40 | junit 41 | junit 42 | 4.12 43 | test 44 | 45 | 46 | org.apache.cxf 47 | cxf-rt-rs-client 48 | 3.1.1 49 | test 50 | 51 | 52 | org.glassfish 53 | javax.json 54 | 1.0.4 55 | test 56 | 57 | 58 | net.wasdev.wlp.starters.rest 59 | provided-pom 60 | 0.0.3 61 | pom 62 | provided 63 | 64 | 65 | net.wasdev.wlp.starters.web 66 | provided-pom 67 | 0.0.3 68 | pom 69 | provided 70 | 71 | 72 | net.wasdev.wlp.starters.rest 73 | runtime-pom 74 | 0.0.3 75 | pom 76 | runtime 77 | 78 | 79 | net.wasdev.wlp.starters.web 80 | runtime-pom 81 | 0.0.3 82 | pom 83 | runtime 84 | 85 | 86 | com.cloudant 87 | cloudant-client 88 | 2.7.0 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-war-plugin 98 | 99 | false 100 | JavaCloudantApp 101 | 102 | 103 | 104 | net.wasdev.wlp.maven.plugins 105 | liberty-maven-plugin 106 | 1.2.1 107 | 108 | 109 | 110 | 111 | 112 | net.wasdev.wlp.maven.plugins 113 | liberty-maven-plugin 114 | 115 | src/main/wlp/server.xml 116 | 117 | ../../../../../${project.build.finalName} 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/main/java/example/nosql/AttachServlet.java: -------------------------------------------------------------------------------- 1 | package example.nosql; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.net.URLEncoder; 6 | 7 | import javax.servlet.ServletException; 8 | import javax.servlet.annotation.MultipartConfig; 9 | import javax.servlet.annotation.WebServlet; 10 | import javax.servlet.http.HttpServlet; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import javax.servlet.http.Part; 14 | 15 | import org.apache.commons.codec.binary.Base64; 16 | 17 | import com.cloudant.client.api.Database; 18 | import com.cloudant.client.api.model.Document; 19 | import com.cloudant.client.api.model.Params; 20 | import com.cloudant.client.org.lightcouch.Attachment; 21 | import com.google.gson.JsonObject; 22 | 23 | @WebServlet("/attach") 24 | @MultipartConfig() 25 | public class AttachServlet extends HttpServlet { 26 | 27 | private static final long serialVersionUID = 1L; 28 | 29 | protected void doPost(HttpServletRequest request, HttpServletResponse response) 30 | throws ServletException, IOException { 31 | Part part = request.getPart("file"); 32 | 33 | String id = request.getParameter("id"); 34 | String name = request.getParameter("name"); 35 | String value = request.getParameter("value"); 36 | String fileName = request.getParameter("filename"); 37 | 38 | Database db = null; 39 | try { 40 | db = CloudantClientMgr.getDB(); 41 | } catch (Exception re) { 42 | re.printStackTrace(); 43 | response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 44 | return; 45 | } 46 | 47 | ResourceServlet servlet = new ResourceServlet(); 48 | 49 | JsonObject resultObject = servlet.create(db, id, name, value, part, fileName); 50 | 51 | System.out.println("Upload completed."); 52 | 53 | response.getWriter().println(resultObject.toString()); 54 | } 55 | 56 | protected void doGet(HttpServletRequest request, HttpServletResponse response) 57 | throws ServletException, IOException { 58 | String id = request.getParameter("id"); 59 | String key = request.getParameter("key"); 60 | 61 | Document fav = CloudantClientMgr.getDB().find(Document.class, id, new Params().attachments()); 62 | 63 | OutputStream output = response.getOutputStream(); 64 | Attachment attachment = fav.getAttachments().get(URLEncoder.encode(key, "UTF-8")); 65 | String attachmentData = attachment.getData(); 66 | response.setContentType(attachment.getContentType()); 67 | output.write(Base64.decodeBase64(attachmentData)); 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/example/nosql/CloudantClientMgr.java: -------------------------------------------------------------------------------- 1 | package example.nosql; 2 | 3 | import java.util.Map.Entry; 4 | import java.util.Set; 5 | 6 | import com.cloudant.client.api.ClientBuilder; 7 | import com.cloudant.client.api.CloudantClient; 8 | import com.cloudant.client.api.Database; 9 | import com.cloudant.client.org.lightcouch.CouchDbException; 10 | import com.google.gson.JsonArray; 11 | import com.google.gson.JsonElement; 12 | import com.google.gson.JsonObject; 13 | import com.google.gson.JsonParser; 14 | 15 | public class CloudantClientMgr { 16 | 17 | private static CloudantClient cloudant = null; 18 | private static Database db = null; 19 | 20 | private static String databaseName = "sample_nosql_db"; 21 | 22 | private static String user = "REPLACE_WITH_CLOUDANT_USERNAME"; 23 | private static String password = "REPLACE_WITH_CLOUDANT_PASSWORD"; 24 | 25 | private static void initClient() { 26 | if (cloudant == null) { 27 | synchronized (CloudantClientMgr.class) { 28 | if (cloudant != null) { 29 | return; 30 | } 31 | cloudant = createClient(); 32 | 33 | } 34 | } 35 | } 36 | 37 | private static CloudantClient createClient() { 38 | String VCAP_SERVICES = System.getenv("VCAP_SERVICES"); 39 | String serviceName = null; 40 | 41 | if (VCAP_SERVICES != null) { 42 | // When running in Bluemix, the VCAP_SERVICES env var will have the credentials for all bound/connected services 43 | // Parse the VCAP JSON structure looking for cloudant. 44 | JsonObject obj = (JsonObject) new JsonParser().parse(VCAP_SERVICES); 45 | Entry dbEntry = null; 46 | Set> entries = obj.entrySet(); 47 | // Look for the VCAP key that holds the cloudant no sql db information 48 | for (Entry eachEntry : entries) { 49 | if (eachEntry.getKey().toLowerCase().contains("cloudant")) { 50 | dbEntry = eachEntry; 51 | break; 52 | } 53 | } 54 | if (dbEntry == null) { 55 | throw new RuntimeException("Could not find cloudantNoSQLDB key in VCAP_SERVICES env variable"); 56 | } 57 | 58 | obj = (JsonObject) ((JsonArray) dbEntry.getValue()).get(0); 59 | serviceName = (String) dbEntry.getKey(); 60 | System.out.println("Service Name - " + serviceName); 61 | 62 | obj = (JsonObject) obj.get("credentials"); 63 | 64 | user = obj.get("username").getAsString(); 65 | password = obj.get("password").getAsString(); 66 | 67 | } else { 68 | System.out.println("VCAP_SERVICES env var doesn't exist: running locally."); 69 | } 70 | 71 | try { 72 | System.out.println("Connecting to Cloudant : " + user); 73 | CloudantClient client = ClientBuilder.account(user) 74 | .username(user) 75 | .password(password) 76 | .build(); 77 | return client; 78 | } catch (CouchDbException e) { 79 | throw new RuntimeException("Unable to connect to repository", e); 80 | } 81 | } 82 | 83 | public static Database getDB() { 84 | if (cloudant == null) { 85 | initClient(); 86 | } 87 | 88 | if (db == null) { 89 | try { 90 | db = cloudant.database(databaseName, true); 91 | } catch (Exception e) { 92 | throw new RuntimeException("DB Not found", e); 93 | } 94 | } 95 | return db; 96 | } 97 | 98 | private CloudantClientMgr() { 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/example/nosql/ResourceServlet.java: -------------------------------------------------------------------------------- 1 | package example.nosql; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.PrintWriter; 8 | import java.net.URLEncoder; 9 | import java.util.Date; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | import javax.servlet.http.Part; 15 | import javax.ws.rs.DELETE; 16 | import javax.ws.rs.FormParam; 17 | import javax.ws.rs.GET; 18 | import javax.ws.rs.POST; 19 | import javax.ws.rs.PUT; 20 | import javax.ws.rs.Path; 21 | import javax.ws.rs.Produces; 22 | import javax.ws.rs.QueryParam; 23 | import javax.ws.rs.core.MediaType; 24 | import javax.ws.rs.core.Response; 25 | 26 | import com.cloudant.client.api.Database; 27 | import com.google.gson.JsonArray; 28 | import com.google.gson.JsonObject; 29 | import com.google.gson.internal.LinkedTreeMap; 30 | 31 | @Path("/favorites") 32 | /** 33 | * CRUD service of todo list table. It uses REST style. 34 | */ 35 | public class ResourceServlet { 36 | 37 | public ResourceServlet() { 38 | } 39 | 40 | @POST 41 | public Response create(@QueryParam("id") Long id, @FormParam("name") String name, @FormParam("value") String value) 42 | throws Exception { 43 | 44 | Database db = null; 45 | try { 46 | db = getDB(); 47 | } catch (Exception re) { 48 | re.printStackTrace(); 49 | return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); 50 | } 51 | 52 | String idString = id == null ? null : id.toString(); 53 | JsonObject resultObject = create(db, idString, name, value, null, null); 54 | 55 | System.out.println("Create Successful."); 56 | 57 | return Response.ok(resultObject.toString()).build(); 58 | } 59 | 60 | protected JsonObject create(Database db, String id, String name, String value, Part part, String fileName) 61 | throws IOException { 62 | 63 | // check if document exist 64 | HashMap obj = (id == null) ? null : db.find(HashMap.class, id); 65 | 66 | if (obj == null) { 67 | // if new document 68 | 69 | id = String.valueOf(System.currentTimeMillis()); 70 | 71 | // create a new document 72 | System.out.println("Creating new document with id : " + id); 73 | Map data = new HashMap(); 74 | data.put("name", name); 75 | data.put("_id", id); 76 | data.put("value", value); 77 | data.put("creation_date", new Date().toString()); 78 | db.save(data); 79 | 80 | // attach the attachment object 81 | obj = db.find(HashMap.class, id); 82 | saveAttachment(db, id, part, fileName, obj); 83 | } else { 84 | // if existing document 85 | // attach the attachment object 86 | saveAttachment(db, id, part, fileName, obj); 87 | 88 | // update other fields in the document 89 | obj = db.find(HashMap.class, id); 90 | obj.put("name", name); 91 | obj.put("value", value); 92 | db.update(obj); 93 | } 94 | 95 | obj = db.find(HashMap.class, id); 96 | 97 | JsonObject resultObject = toJsonObject(obj); 98 | 99 | return resultObject; 100 | } 101 | 102 | @GET 103 | @Produces(MediaType.APPLICATION_JSON) 104 | public Response get(@QueryParam("id") Long id, @QueryParam("cmd") String cmd) throws Exception { 105 | 106 | Database db = null; 107 | try { 108 | db = getDB(); 109 | } catch (Exception re) { 110 | re.printStackTrace(); 111 | return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); 112 | } 113 | 114 | JsonObject resultObject = new JsonObject(); 115 | JsonArray jsonArray = new JsonArray(); 116 | 117 | if (id == null) { 118 | try { 119 | // get all the document present in database 120 | List allDocs = db.getAllDocsRequestBuilder().includeDocs(true).build().getResponse() 121 | .getDocsAs(HashMap.class); 122 | 123 | if (allDocs.size() == 0) { 124 | allDocs = initializeSampleData(db); 125 | } 126 | 127 | for (HashMap doc : allDocs) { 128 | HashMap obj = db.find(HashMap.class, doc.get("_id") + ""); 129 | JsonObject jsonObject = new JsonObject(); 130 | LinkedTreeMap attachments = (LinkedTreeMap) obj.get("_attachments"); 131 | 132 | if (attachments != null && attachments.size() > 0) { 133 | JsonArray attachmentList = getAttachmentList(attachments, obj.get("_id") + ""); 134 | jsonObject.addProperty("id", obj.get("_id") + ""); 135 | jsonObject.addProperty("name", obj.get("name") + ""); 136 | jsonObject.addProperty("value", obj.get("value") + ""); 137 | jsonObject.add("attachements", attachmentList); 138 | 139 | } else { 140 | jsonObject.addProperty("id", obj.get("_id") + ""); 141 | jsonObject.addProperty("name", obj.get("name") + ""); 142 | jsonObject.addProperty("value", obj.get("value") + ""); 143 | } 144 | 145 | jsonArray.add(jsonObject); 146 | } 147 | } catch (Exception dnfe) { 148 | System.out.println("Exception thrown : " + dnfe.getMessage()); 149 | } 150 | 151 | resultObject.addProperty("id", "all"); 152 | resultObject.add("body", jsonArray); 153 | 154 | return Response.ok(resultObject.toString()).build(); 155 | } 156 | 157 | // check if document exists 158 | HashMap obj = db.find(HashMap.class, id + ""); 159 | if (obj != null) { 160 | JsonObject jsonObject = toJsonObject(obj); 161 | return Response.ok(jsonObject.toString()).build(); 162 | } else { 163 | return Response.status(Response.Status.NOT_FOUND).build(); 164 | } 165 | } 166 | 167 | @DELETE 168 | public Response delete(@QueryParam("id") long id) { 169 | 170 | Database db = null; 171 | try { 172 | db = getDB(); 173 | } catch (Exception re) { 174 | re.printStackTrace(); 175 | return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); 176 | } 177 | 178 | // check if document exist 179 | HashMap obj = db.find(HashMap.class, id + ""); 180 | 181 | if (obj == null) { 182 | return Response.status(Response.Status.NOT_FOUND).build(); 183 | } else { 184 | db.remove(obj); 185 | 186 | System.out.println("Delete Successful."); 187 | 188 | return Response.ok("OK").build(); 189 | } 190 | } 191 | 192 | @PUT 193 | public Response update(@QueryParam("id") long id, @QueryParam("name") String name, 194 | @QueryParam("value") String value) { 195 | 196 | Database db = null; 197 | try { 198 | db = getDB(); 199 | } catch (Exception re) { 200 | re.printStackTrace(); 201 | return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); 202 | } 203 | 204 | // check if document exist 205 | HashMap obj = db.find(HashMap.class, id + ""); 206 | 207 | if (obj == null) { 208 | return Response.status(Response.Status.NOT_FOUND).build(); 209 | } else { 210 | obj.put("name", name); 211 | obj.put("value", value); 212 | 213 | db.update(obj); 214 | 215 | System.out.println("Update Successful."); 216 | 217 | return Response.ok("OK").build(); 218 | } 219 | } 220 | 221 | private JsonArray getAttachmentList(LinkedTreeMap attachmentList, String docID) { 222 | JsonArray attachmentArray = new JsonArray(); 223 | 224 | for (Object key : attachmentList.keySet()) { 225 | LinkedTreeMap attach = (LinkedTreeMap) attachmentList.get(key); 226 | 227 | JsonObject attachedObject = new JsonObject(); 228 | // set the content type of the attachment 229 | attachedObject.addProperty("content_type", attach.get("content_type").toString()); 230 | // append the document id and attachment key to the URL 231 | attachedObject.addProperty("url", "attach?id=" + docID + "&key=" + key); 232 | // set the key of the attachment 233 | attachedObject.addProperty("key", key + ""); 234 | 235 | // add the attachment object to the array 236 | attachmentArray.add(attachedObject); 237 | } 238 | 239 | return attachmentArray; 240 | } 241 | 242 | private JsonObject toJsonObject(HashMap obj) { 243 | JsonObject jsonObject = new JsonObject(); 244 | LinkedTreeMap attachments = (LinkedTreeMap) obj.get("_attachments"); 245 | if (attachments != null && attachments.size() > 0) { 246 | JsonArray attachmentList = getAttachmentList(attachments, obj.get("_id") + ""); 247 | jsonObject.add("attachements", attachmentList); 248 | } 249 | jsonObject.addProperty("id", obj.get("_id") + ""); 250 | jsonObject.addProperty("name", obj.get("name") + ""); 251 | jsonObject.addProperty("value", obj.get("value") + ""); 252 | return jsonObject; 253 | } 254 | 255 | private void saveAttachment(Database db, String id, Part part, String fileName, HashMap obj) 256 | throws IOException { 257 | if (part != null) { 258 | InputStream inputStream = part.getInputStream(); 259 | try { 260 | db.saveAttachment(inputStream, URLEncoder.encode(fileName,"UTF-8"), part.getContentType(), id, (String) obj.get("_rev")); 261 | } finally { 262 | inputStream.close(); 263 | } 264 | } 265 | } 266 | 267 | /* 268 | * Create a document and Initialize with sample data/attachments 269 | */ 270 | private List initializeSampleData(Database db) throws Exception { 271 | 272 | long id = System.currentTimeMillis(); 273 | String name = "Sample category"; 274 | String value = "List of sample files"; 275 | 276 | // create a new document 277 | System.out.println("Creating new document with id : " + id); 278 | Map data = new HashMap(); 279 | data.put("name", name); 280 | data.put("_id", id + ""); 281 | data.put("value", value); 282 | data.put("creation_date", new Date().toString()); 283 | db.save(data); 284 | 285 | // attach the object 286 | HashMap obj = db.find(HashMap.class, id + ""); 287 | 288 | // attachment#1 289 | File file = new File("Sample.txt"); 290 | file.createNewFile(); 291 | PrintWriter writer = new PrintWriter(file); 292 | writer.write("This is a sample file..."); 293 | writer.flush(); 294 | writer.close(); 295 | FileInputStream fileInputStream = new FileInputStream(file); 296 | db.saveAttachment(fileInputStream, file.getName(), "text/plain", id + "", (String) obj.get("_rev")); 297 | fileInputStream.close(); 298 | 299 | return db.getAllDocsRequestBuilder().includeDocs(true).build().getResponse().getDocsAs(HashMap.class); 300 | 301 | } 302 | 303 | private Database getDB() { 304 | return CloudantClientMgr.getDB(); 305 | } 306 | 307 | } 308 | -------------------------------------------------------------------------------- /src/main/webapp/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Cloudant DB Web Starter 7 | 8 | javax.ws.rs.core.Application 9 | 1 10 | 11 | 12 | javax.ws.rs.core.Application 13 | /api/* 14 | 15 | 16 | 17 | index.html 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/webapp/images/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Cloud/java-cloudant/4fe2ae82ddd3a8c899cb196445b9bc3253567992/src/main/webapp/images/add.png -------------------------------------------------------------------------------- /src/main/webapp/images/detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Cloud/java-cloudant/4fe2ae82ddd3a8c899cb196445b9bc3253567992/src/main/webapp/images/detail.png -------------------------------------------------------------------------------- /src/main/webapp/images/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Cloud/java-cloudant/4fe2ae82ddd3a8c899cb196445b9bc3253567992/src/main/webapp/images/info.png -------------------------------------------------------------------------------- /src/main/webapp/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Cloud/java-cloudant/4fe2ae82ddd3a8c899cb196445b9bc3253567992/src/main/webapp/images/loading.gif -------------------------------------------------------------------------------- /src/main/webapp/images/newapp-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Cloud/java-cloudant/4fe2ae82ddd3a8c899cb196445b9bc3253567992/src/main/webapp/images/newapp-icon.png -------------------------------------------------------------------------------- /src/main/webapp/images/remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Cloud/java-cloudant/4fe2ae82ddd3a8c899cb196445b9bc3253567992/src/main/webapp/images/remove.png -------------------------------------------------------------------------------- /src/main/webapp/images/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Cloud/java-cloudant/4fe2ae82ddd3a8c899cb196445b9bc3253567992/src/main/webapp/images/sprite.png -------------------------------------------------------------------------------- /src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Favorites Organizer powered by Cloudant 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 18 | 19 |
20 |
21 |
22 |
23 |
24 | 27 |
28 | Help 29 |
30 | 39 |
40 |




41 |
42 |
43 | 44 | 45 |
46 |


47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /src/main/webapp/index.js: -------------------------------------------------------------------------------- 1 | // index.js 2 | 3 | var REST_DATA = 'api/favorites'; 4 | 5 | var KEY_ENTER = 13; 6 | var defaultItems = [ 7 | 8 | ]; 9 | 10 | function encodeUriAndQuotes(untrustedStr) { 11 | return encodeURI(String(untrustedStr)).replace(/'/g, '%27').replace(')', '%29'); 12 | } 13 | 14 | function loadItems(){ 15 | xhrGet(REST_DATA, function(data){ 16 | 17 | //stop showing loading message 18 | stopLoadingMessage(); 19 | 20 | var receivedItems = data.body || []; 21 | var items = []; 22 | var i; 23 | // Make sure the received items have correct format 24 | for(i = 0; i < receivedItems.length; ++i){ 25 | var item = receivedItems[i]; 26 | if(item && 'id' in item){ 27 | items.push(item); 28 | } 29 | } 30 | var hasItems = items.length; 31 | if(!hasItems){ 32 | items = defaultItems; 33 | } 34 | for(i = 0; i < items.length; ++i){ 35 | addItem(items[i], !hasItems); 36 | } 37 | if(!hasItems){ 38 | var table = document.getElementById('notes'); 39 | var nodes = []; 40 | for(i = 0; i < table.rows.length; ++i){ 41 | nodes.push(table.rows[i].firstChild.firstChild); 42 | } 43 | function save(){ 44 | if(nodes.length){ 45 | saveChange(nodes.shift(), save); 46 | } 47 | } 48 | save(); 49 | } 50 | }, function(err){ 51 | console.log(err); 52 | //stop showing loading message 53 | stopLoadingMessage(); 54 | document.getElementById('errorDiv').innerHTML = err; 55 | 56 | }); 57 | } 58 | 59 | function startProgressIndicator(row) 60 | { 61 | row.innerHTML="Uploading file... "; 62 | } 63 | 64 | function removeProgressIndicator(row) 65 | { 66 | row.innerHTML="uploaded..."; 67 | } 68 | 69 | function addNewRow(table) 70 | { 71 | var newRow = document.createElement('tr'); 72 | table.appendChild(newRow); 73 | return table.lastChild; 74 | } 75 | 76 | function uploadFile(node) { 77 | 78 | var file = node.previousSibling.files[0]; 79 | 80 | //if file not selected, throw error 81 | if (!file) { 82 | alert("File not selected for upload... \t\t\t\t \n\n - Choose a file to upload. \n - Then click on Upload button."); 83 | return; 84 | } 85 | 86 | var row = node.parentNode.parentNode.parentNode; 87 | 88 | var form = new FormData(); 89 | form.append("file", file); 90 | 91 | var id = row.getAttribute('data-id'); 92 | 93 | var queryParams = (id == null) ? "" : "id=" + id; 94 | queryParams += "&name=" + row.firstChild.firstChild.value; 95 | queryParams += "&value=" + row.firstChild.nextSibling.firstChild.value; 96 | queryParams+= "&filename="+file.name; 97 | 98 | 99 | var table = row.firstChild.nextSibling.firstChild; 100 | var newRow = addNewRow(table); 101 | 102 | startProgressIndicator(newRow); 103 | 104 | xhrAttach("attach?"+queryParams, form, function(item){ 105 | console.log('Item id - ' + item.id); 106 | console.log('attached: ', item); 107 | row.setAttribute('data-id', item.id); 108 | removeProgressIndicator(row); 109 | setRowContent(item, row); 110 | }, function(err) { 111 | console.error(err); 112 | }); 113 | 114 | } 115 | 116 | var attachButton = "
"; 117 | function sanitizeInput(str) { 118 | return String(str).replace(/&(?!amp;|lt;|gt;)/g, '&').replace(//g, '>'); 119 | } 120 | function setRowContent(item, row) { 121 | var innerHTML = ""; 122 | 123 | var valueTextArea = ""; 124 | if (item.value) { 125 | valueTextArea = ""; 126 | } 127 | 128 | innerHTML += valueTextArea; 129 | 130 | 131 | var attachments = item.attachements; 132 | if (attachments && attachments.length > 0) { 133 | innerHTML += "
"; 134 | for (var i = 0; i < attachments.length; ++i) { 135 | var attachment = attachments[i]; 136 | 137 | if (attachment.content_type.indexOf("image/") == 0) { 138 | innerHTML += "
" + attachment.key + "
"; 139 | 140 | } else if (attachment.content_type.indexOf("audio/") == 0) { 141 | innerHTML += "
" + attachment.key + "
"; 142 | 143 | } else if (attachment.content_type.indexOf("video/") == 0) { 144 | innerHTML += "
" + attachment.key + "
"; 145 | 146 | } else if (attachment.content_type.indexOf("text/") == 0 || attachment.content_type.indexOf("application/") == 0) { 147 | innerHTML += ""; 148 | } 149 | 150 | } 151 | innerHTML += "
"; 152 | 153 | } 154 | 155 | row.innerHTML = innerHTML + attachButton + ""; 156 | 157 | } 158 | 159 | function addItem(item, isNew) { 160 | 161 | var row = document.createElement('tr'); 162 | row.className = "tableRows"; 163 | var id = item && item.id; 164 | if (id) { 165 | row.setAttribute('data-id', id); 166 | } 167 | 168 | 169 | 170 | if (item) // if not a new row 171 | { 172 | setRowContent(item, row); 173 | } else //if new row 174 | { 175 | row.innerHTML = "" + attachButton + "" + 176 | ""; 177 | } 178 | 179 | var table = document.getElementById('notes'); 180 | table.lastChild.appendChild(row); 181 | row.isNew = !item || isNew; 182 | 183 | if (row.isNew) { 184 | var textarea = row.firstChild.firstChild; 185 | textarea.focus(); 186 | } 187 | 188 | } 189 | 190 | function deleteItem(deleteBtnNode){ 191 | var row = deleteBtnNode.parentNode.parentNode; 192 | if(row.getAttribute('data-id')) 193 | { 194 | row.remove(); 195 | xhrDelete(REST_DATA + '?id=' + row.getAttribute('data-id'), function(){ 196 | }, function(err){ 197 | console.log(err); 198 | //stop showing loading message 199 | stopLoadingMessage(); 200 | document.getElementById('errorDiv').innerHTML = err; 201 | }); 202 | } 203 | } 204 | 205 | 206 | function onKey(evt) { 207 | 208 | if (evt.keyCode == KEY_ENTER && !evt.shiftKey) { 209 | 210 | evt.stopPropagation(); 211 | evt.preventDefault(); 212 | var nameV, valueV; 213 | var row; 214 | 215 | if (evt.target.id == "nameText") { 216 | row = evt.target.parentNode.parentNode; 217 | nameV = evt.target.value; 218 | valueV = row.firstChild.nextSibling.firstChild.value; 219 | 220 | } else { 221 | row = evt.target.parentNode.parentNode; 222 | nameV = row.firstChild.firstChild.value; 223 | valueV = evt.target.value; 224 | } 225 | 226 | var data = { 227 | name: nameV, 228 | value: valueV 229 | }; 230 | 231 | if (row.isNew) { 232 | delete row.isNew; 233 | xhrPost(REST_DATA, data, function(item){ 234 | row.setAttribute('data-id', item.id); 235 | }, function(err){ 236 | console.log(err); 237 | //stop showing loading message 238 | stopLoadingMessage(); 239 | document.getElementById('errorDiv').innerHTML = err; 240 | }); 241 | } else { 242 | var requestParam = '?id=' + row.getAttribute('data-id')+"&name="+nameV+"&value="+valueV; 243 | xhrPut(REST_DATA+requestParam, data, function(){ 244 | console.log('updated: ', data); 245 | }, function(err){ 246 | console.log(err); 247 | //stop showing loading message 248 | stopLoadingMessage(); 249 | document.getElementById('errorDiv').innerHTML = err; 250 | }); 251 | } 252 | 253 | 254 | if (row.nextSibling) { 255 | row.nextSibling.firstChild.firstChild.focus(); 256 | } else { 257 | addItem(); 258 | } 259 | } 260 | } 261 | 262 | function saveChange(contentNode, callback){ 263 | var row = contentNode.parentNode.parentNode; 264 | 265 | var data = { 266 | name: row.firstChild.firstChild.value, 267 | value:row.firstChild.nextSibling.firstChild.value 268 | }; 269 | 270 | if(row.isNew){ 271 | delete row.isNew; 272 | xhrPost(REST_DATA, data, function(item){ 273 | row.setAttribute('data-id', item.id); 274 | callback && callback(); 275 | }, function(err){ 276 | console.log(err); 277 | //stop showing loading message 278 | stopLoadingMessage(); 279 | document.getElementById('errorDiv').innerHTML = err; 280 | }); 281 | }else{ 282 | data.id = row.getAttribute('data-id'); 283 | xhrPut(REST_DATA, data, function(){ 284 | console.log('updated: ', data); 285 | }, function(err){ 286 | console.log(err); 287 | //stop showing loading message 288 | stopLoadingMessage(); 289 | document.getElementById('errorDiv').innerHTML = err; 290 | }); 291 | } 292 | } 293 | 294 | 295 | 296 | function toggleAppInfo(){ 297 | var node = document.getElementById('appinfo'); 298 | node.style.display = node.style.display == 'none' ? '' : 'none'; 299 | } 300 | 301 | 302 | function showLoadingMessage() 303 | { 304 | document.getElementById('loadingImage').innerHTML = "Loading data "+""; 305 | } 306 | function stopLoadingMessage() 307 | { 308 | document.getElementById('loadingImage').innerHTML = ""; 309 | } 310 | 311 | showLoadingMessage(); 312 | loadItems(); 313 | 314 | -------------------------------------------------------------------------------- /src/main/webapp/style.css: -------------------------------------------------------------------------------- 1 | /* style.css 2 | * This file provides css styles. 3 | */ 4 | 5 | body,html { 6 | background-color: #f5f7fa; 7 | width: 100%; 8 | height: 100%; 9 | margin: 0 auto; 10 | font-family: Lato,sans-serif; 11 | color: #283D56; 12 | } 13 | 14 | /* header footer common */ 15 | header, footer { 16 | width: 100%; 17 | text-align: center; 18 | } 19 | 20 | .container { 21 | height: 100%; 22 | overflow: auto; 23 | display: flex; 24 | flex-direction: column; 25 | } 26 | 27 | .banner { 28 | background: #5586D6; 29 | color: #fff; 30 | padding: 10px; 31 | } 32 | 33 | /* positions */ 34 | header { 35 | padding-top: 20px; 36 | font-size: 1.3em; 37 | } 38 | footer { 39 | padding: 10px; 40 | font-size: .8rem; 41 | } 42 | .title, .tips { 43 | height: 30px; 44 | padding-top: 20px; 45 | position: relative; 46 | } 47 | .title { 48 | border-radius: 40px 40px 0 0; 49 | } 50 | .tips { 51 | border-radius: 0 0 40px 40px; 52 | } 53 | 54 | table { 55 | table-layout: fixed; 56 | word-wrap: break-word; 57 | position: relative; 58 | border-collapse: collapse; 59 | width: 100%; 60 | } 61 | .content { 62 | font-size: .8rem; 63 | width: 30%; 64 | vertical-align: top; 65 | } 66 | .contentName { 67 | padding: 10px; 68 | padding-left: 20px; 69 | width: 25%; 70 | font-weight:bold; 71 | vertical-align: top; 72 | } 73 | .contentDetails { 74 | padding: 10px; 75 | } 76 | .contentAction { 77 | width: 10%; 78 | padding-right: 20px; 79 | text-align: right; 80 | } 81 | 82 | .nameText { 83 | font-weight: bold; 84 | } 85 | .records textarea { 86 | height: 1.2em; 87 | font-size: 1em; 88 | resize: none; 89 | width: 100%; 90 | border: none; 91 | overflow: hidden; 92 | background-color: transparent; 93 | font-size: .9em; 94 | } 95 | 96 | /* buttons */ 97 | .detailBtn { 98 | position: absolute; 99 | left: 20px; 100 | top: 10px; 101 | background: none; 102 | border: none; 103 | cursor: pointer; 104 | } 105 | .addBtn { 106 | background: none; 107 | border: none; 108 | cursor: pointer; 109 | } 110 | .detailBtn img{ 111 | width: 20px; 112 | height: 20px; 113 | } 114 | .deleteBtn{ 115 | background-image: url("images/sprite.png"); 116 | background-position: -5px -568px; 117 | width: 18px; 118 | height: 18px; 119 | display:inline-block; 120 | } 121 | 122 | tr:hover .deleteBtn { 123 | 124 | cursor: pointer; 125 | } 126 | 127 | 128 | .tableRows{ 129 | border: 2px solid #DFE3E6 130 | } 131 | 132 | 133 | 134 | .fieldname { 135 | text-align: right; 136 | border-right: 1px solid #fff; 137 | } 138 | 139 | .errorMsg { 140 | color: red; 141 | font-weight:normal; 142 | } 143 | .newappIcon { 144 | display: block; 145 | max-width: 100px; 146 | max-height: 100px; 147 | margin: 0 auto; 148 | padding: 10px; 149 | } 150 | 151 | .appTitle { 152 | padding: 15px; 153 | margin: 0 auto; 154 | text-align: center; 155 | font-size: 1.5rem; 156 | } 157 | 158 | .description { 159 | padding-left: 50px; 160 | padding-right: 50px; 161 | text-align: center; 162 | } 163 | 164 | .infoImg { 165 | cursor: pointer; 166 | } 167 | .flexBox { 168 | display: flex; 169 | flex-wrap: wrap; 170 | } 171 | .contentTiles { 172 | padding: 10px; 173 | font-size: .8rem; 174 | } 175 | .uploadBox { 176 | margin: 0 auto; 177 | text-align: center; 178 | } 179 | .appinfo { 180 | padding: 10px; 181 | } 182 | -------------------------------------------------------------------------------- /src/main/webapp/util.js: -------------------------------------------------------------------------------- 1 | //utilities 2 | function createXHR(){ 3 | if(typeof XMLHttpRequest != 'undefined'){ 4 | return new XMLHttpRequest(); 5 | }else{ 6 | try{ 7 | return new ActiveXObject('Msxml2.XMLHTTP'); 8 | }catch(e){ 9 | try{ 10 | return new ActiveXObject('Microsoft.XMLHTTP'); 11 | }catch(e){} 12 | } 13 | } 14 | return null; 15 | } 16 | 17 | function xhrGet(url, callback, errback){ 18 | var xhr = new createXHR(); 19 | xhr.open("GET", url, true); 20 | xhr.onreadystatechange = function(){ 21 | if(xhr.readyState === 4){ 22 | if(xhr.status === 200){ 23 | callback(parseJson(xhr.responseText)); 24 | }else{ 25 | errback("Error: "+xhr.responseText); 26 | } 27 | } 28 | }; 29 | 30 | xhr.timeout = 100000; 31 | xhr.ontimeout = errback; 32 | xhr.send(); 33 | } 34 | 35 | function xhrPut(url, data, callback, errback){ 36 | var xhr = new createXHR(); 37 | xhr.open("PUT", url, true); 38 | xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 39 | xhr.onreadystatechange = function(){ 40 | if(xhr.readyState === 4){ 41 | if(xhr.status === 200){ 42 | callback(); 43 | }else{ 44 | errback("Error: "+xhr.responseText); 45 | } 46 | } 47 | }; 48 | xhr.timeout = 100000; 49 | xhr.ontimeout = errback; 50 | xhr.send(objectToQuery(data)); 51 | } 52 | 53 | function xhrAttach(url, data, callback, errback) 54 | { 55 | var xhr = new createXHR(); 56 | xhr.open("POST", url, true); 57 | //xhr.setRequestHeader("Content-type", "multipart/form-data"); 58 | xhr.onreadystatechange = function(){ 59 | if(xhr.readyState === 4){ 60 | if(xhr.status === 200){ 61 | callback(parseJson(xhr.responseText)); 62 | }else{ 63 | errback("Error: "+xhr.responseText); 64 | } 65 | } 66 | }; 67 | xhr.timeout = 1000000; 68 | xhr.ontimeout = errback; 69 | xhr.send(data); 70 | } 71 | 72 | function xhrPost(url, data, callback, errback){ 73 | var xhr = new createXHR(); 74 | xhr.open("POST", url, true); 75 | xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 76 | xhr.onreadystatechange = function(){ 77 | if(xhr.readyState === 4){ 78 | if(xhr.status === 200){ 79 | callback(parseJson(xhr.responseText)); 80 | }else{ 81 | errback("Error: "+xhr.responseText); 82 | } 83 | } 84 | }; 85 | xhr.timeout = 100000; 86 | xhr.ontimeout = errback; 87 | xhr.send(objectToQuery(data)); 88 | } 89 | 90 | function xhrDelete(url, callback, errback){ 91 | var xhr = new createXHR(); 92 | xhr.open("DELETE", url, true); 93 | xhr.onreadystatechange = function(){ 94 | if(xhr.readyState === 4){ 95 | if(xhr.status === 200){ 96 | callback(); 97 | }else{ 98 | errback("Error: "+xhr.responseText); 99 | } 100 | } 101 | }; 102 | xhr.timeout = 100000; 103 | xhr.ontimeout = errback; 104 | xhr.send(); 105 | } 106 | 107 | function parseJson(str){ 108 | return JSON.parse(str); 109 | } 110 | 111 | function objectToQuery(map){ 112 | var enc = encodeURIComponent, pairs = []; 113 | for(var name in map){ 114 | var value = map[name]; 115 | var assign = enc(name) + "="; 116 | if(value && value instanceof Array){ 117 | for(var i = 0, len = value.length; i < len; ++i){ 118 | pairs.push(assign + enc(value[i])); 119 | } 120 | }else{ 121 | pairs.push(assign + enc(value)); 122 | } 123 | } 124 | return pairs.join("&"); 125 | } 126 | -------------------------------------------------------------------------------- /src/main/wlp/server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | webProfile-7.0 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /target/JavaCloudantApp.war: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Cloud/java-cloudant/4fe2ae82ddd3a8c899cb196445b9bc3253567992/target/JavaCloudantApp.war --------------------------------------------------------------------------------