* Added support for Java Deployment with Maven

* Added support for Java Deployment with Maven

* @matrixjnr/feature:java support

* @matrixjnr/feature:java support
pull/85/head
John Simiyu 2019-08-11 00:00:40 +03:00 zatwierdzone przez Rui Carmo
rodzic 61a5648214
commit bfc4f8ce32
39 zmienionych plików z 1492 dodań i 6 usunięć

2
.gitignore vendored
Wyświetl plik

@ -53,4 +53,4 @@ coverage.xml
docs/_build/
# PyBuilder
target/
target/

3
.vscode/settings.json vendored 100644
Wyświetl plik

@ -0,0 +1,3 @@
{
"python.pythonPath": "/usr/bin/python3"
}

Wyświetl plik

@ -39,6 +39,7 @@ If you put the `piku-bootstrap` script on your `PATH` somewhere, you can use it
* For Python, it segregates each app's dependencies into a `virtualenv`.
* For Go, it defines a separate `GOPATH` for each app.
* For Node, it installs whatever is in `package.json` into `node_modules`.
* For Java, it builds your app depending on either `pom.xml` or `build.gradle` file.
* It then looks at a `Procfile` and starts the relevant workers using [uWSGI][uwsgi] as a generic process manager.
* You can then remotely change application settings (`config:set`) or scale up/down worker processes (`ps:scale`) at will.
* You can also bake application settings into a file called [`ENV` which is documented here](./docs/ENV.md).
@ -89,6 +90,7 @@ From the bottom up:
- [ ] Review deployment messages
- [ ] WIP: Review docs/CLI command documentation (short descriptions done, need `help <cmd>` and better descriptions)
- [ ] Lua/WSAPI support
- [x] Support for Java Apps with maven/gradle (in progress through jwsgi, by @matrixjnr)
- [x] Django and Wisp examples (by @chr15m)
- [x] Project logo (by @chr15m)
- [x] Various release/deployment improvements (by @chr15m)

135
examples/spark-app/.gitignore vendored 100644
Wyświetl plik

@ -0,0 +1,135 @@
# Created by https://www.gitignore.io
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Java ###
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
target
# Created by https://www.gitignore.io/api/gradle
### Gradle ###
.gradle
build/
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Cache of project
.gradletasknamecache
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
# Avoid Maven target files
target/

Wyświetl plik

@ -0,0 +1,5 @@
AUTORESTART=1
RANGE=10
PORT=6500
BIND_ADDRESS=0.0.0.0
INTERVAL=1

Wyświetl plik

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 David Åse
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Wyświetl plik

@ -0,0 +1 @@
jwsgi: mvn exec:java -Dexec.mainClass=app.Application

Wyświetl plik

@ -0,0 +1,30 @@
# spark-basic-structure
[![asciicast](https://asciinema.org/a/257670.svg)](https://asciinema.org/a/257670)
Java on Piku Micro PaaS on Ubuntu 18 and Linux Mint 19
This is an example of one possible way of structuring a Spark application.
This is a simple Java app that demonstrates deploying your apps on Piku.
To publish this app to `piku`, make a copy of this folder and run the following commands:
```bash
cd sparkAppp_copy
git init .
git remote add piku piku@your_server:sparkApp
git add .
git commit -a -m "initial commit"
git push piku master
```
The application has filters, controllers, views, authentication, localization, error handling, and more.
It contains the source code for the tutorial found at https://sparktutorials.github.io/2016/06/10/spark-basic-structure.html
## Some Issues
Default procfile should look exactly like this one.
The live config needs the app directory under virtual environment.... Please do not specify the virtual environment in the ENV file.
## Screenshot
![Application Screenshot](https://sparktutorials.github.io/img/posts/sparkBasicStructure/screenshot.png)

Wyświetl plik

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>spark-basic-structure</groupId>
<artifactId>spark-basic-structure</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>app.Application</mainClass>
<arguments>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-debug-tools</artifactId>
<version>0.5</version>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-template-velocity</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.3m</version>
</dependency>
</dependencies>
</project>

Wyświetl plik

@ -0,0 +1,47 @@
package app;
import app.book.*;
import app.index.*;
import app.login.*;
import app.user.*;
import app.util.*;
import static spark.Spark.*;
import static spark.debug.DebugScreen.*;
public class Application {
// Declare dependencies
public static BookDao bookDao;
public static UserDao userDao;
public static void main(String[] args) {
// Instantiate your dependencies
bookDao = new BookDao();
userDao = new UserDao();
// Configure Spark
port(6500);
staticFiles.location("/public");
staticFiles.expireTime(600L);
enableDebugScreen();
// Set up before-filters (called before each get/post)
before("*", Filters.addTrailingSlashes);
before("*", Filters.handleLocaleChange);
// Set up routes
get(Path.Web.INDEX, IndexController.serveIndexPage);
get(Path.Web.BOOKS, BookController.fetchAllBooks);
get(Path.Web.ONE_BOOK, BookController.fetchOneBook);
get(Path.Web.LOGIN, LoginController.serveLoginPage);
post(Path.Web.LOGIN, LoginController.handleLoginPost);
post(Path.Web.LOGOUT, LoginController.handleLogoutPost);
get("*", ViewUtil.notFound);
//Set up after-filters (called after each get/post)
after("*", Filters.addGzipHeader);
}
}

Wyświetl plik

@ -0,0 +1,18 @@
package app.book;
import lombok.*;
@Value // All fields are private and final. Getters (but not setters) are generated (https://projectlombok.org/features/Value.html)
public class Book {
String title;
String author;
String isbn;
public String getMediumCover() {
return "http://covers.openlibrary.org/b/isbn/" + this.isbn + "-M.jpg";
}
public String getLargeCover() {
return "http://covers.openlibrary.org/b/isbn/" + this.isbn + "-L.jpg";
}
}

Wyświetl plik

@ -0,0 +1,39 @@
package app.book;
import app.login.*;
import app.util.*;
import spark.*;
import java.util.*;
import static app.Application.bookDao;
import static app.util.JsonUtil.*;
import static app.util.RequestUtil.*;
public class BookController {
public static Route fetchAllBooks = (Request request, Response response) -> {
LoginController.ensureUserIsLoggedIn(request, response);
if (clientAcceptsHtml(request)) {
HashMap<String, Object> model = new HashMap<>();
model.put("books", bookDao.getAllBooks());
return ViewUtil.render(request, model, Path.Template.BOOKS_ALL);
}
if (clientAcceptsJson(request)) {
return dataToJson(bookDao.getAllBooks());
}
return ViewUtil.notAcceptable.handle(request, response);
};
public static Route fetchOneBook = (Request request, Response response) -> {
LoginController.ensureUserIsLoggedIn(request, response);
if (clientAcceptsHtml(request)) {
HashMap<String, Object> model = new HashMap<>();
Book book = bookDao.getBookByIsbn(getParamIsbn(request));
model.put("book", book);
return ViewUtil.render(request, model, Path.Template.BOOKS_ONE);
}
if (clientAcceptsJson(request)) {
return dataToJson(bookDao.getBookByIsbn(getParamIsbn(request)));
}
return ViewUtil.notAcceptable.handle(request, response);
};
}

Wyświetl plik

@ -0,0 +1,34 @@
package app.book;
import com.google.common.collect.*;
import java.util.*;
public class BookDao {
private final List<Book> books = ImmutableList.of(
new Book("Moby Dick", "Herman Melville", "9789583001215"),
new Book("A Christmas Carol", "Charles Dickens", "9780141324524"),
new Book("Pride and Prejudice", "Jane Austen", "9781936594290"),
new Book("The Fellowship of The Ring", "J. R. R. Tolkien", "0007171978"),
new Book("Harry Potter", "J. K. Rowling", "0747532699"),
new Book("War and Peace", "Leo Tolstoy", "9780060798871"),
new Book("Don Quixote", "Miguel Cervantes", "9789626345221"),
new Book("Ulysses", "James Joyce", "9780394703800"),
new Book("The Great Gatsby", "F. Scott Fitzgerald", "9780743273565"),
new Book("One Hundred Years of Solitude", "Gabriel Garcia Marquez", "9780060531041"),
new Book("The adventures of Huckleberry Finn", "Mark Twain", "9781591940296"),
new Book("Alice In Wonderland", "Lewis Carrol", "9780439291491")
);
public Iterable<Book> getAllBooks() {
return books;
}
public Book getBookByIsbn(String isbn) {
return books.stream().filter(b -> b.getIsbn().equals(isbn)).findFirst().orElse(null);
}
public Book getRandomBook() {
return books.get(new Random().nextInt(books.size()));
}
}

Wyświetl plik

@ -0,0 +1,15 @@
package app.index;
import app.util.*;
import spark.*;
import java.util.*;
import static app.Application.*;
public class IndexController {
public static Route serveIndexPage = (Request request, Response response) -> {
Map<String, Object> model = new HashMap<>();
model.put("users", userDao.getAllUserNames());
model.put("book", bookDao.getRandomBook());
return ViewUtil.render(request, model, Path.Template.INDEX);
};
}

Wyświetl plik

@ -0,0 +1,48 @@
package app.login;
import app.user.*;
import app.util.*;
import spark.*;
import java.util.*;
import static app.util.RequestUtil.*;
public class LoginController {
public static Route serveLoginPage = (Request request, Response response) -> {
Map<String, Object> model = new HashMap<>();
model.put("loggedOut", removeSessionAttrLoggedOut(request));
model.put("loginRedirect", removeSessionAttrLoginRedirect(request));
return ViewUtil.render(request, model, Path.Template.LOGIN);
};
public static Route handleLoginPost = (Request request, Response response) -> {
Map<String, Object> model = new HashMap<>();
if (!UserController.authenticate(getQueryUsername(request), getQueryPassword(request))) {
model.put("authenticationFailed", true);
return ViewUtil.render(request, model, Path.Template.LOGIN);
}
model.put("authenticationSucceeded", true);
request.session().attribute("currentUser", getQueryUsername(request));
if (getQueryLoginRedirect(request) != null) {
response.redirect(getQueryLoginRedirect(request));
}
return ViewUtil.render(request, model, Path.Template.LOGIN);
};
public static Route handleLogoutPost = (Request request, Response response) -> {
request.session().removeAttribute("currentUser");
request.session().attribute("loggedOut", true);
response.redirect(Path.Web.LOGIN);
return null;
};
// The origin of the request (request.pathInfo()) is saved in the session so
// the user can be redirected back after login
public static void ensureUserIsLoggedIn(Request request, Response response) {
if (request.session().attribute("currentUser") == null) {
request.session().attribute("loginRedirect", request.pathInfo());
response.redirect(Path.Web.LOGIN);
}
};
}

Wyświetl plik

@ -0,0 +1,10 @@
package app.user;
import lombok.*;
@Value // All fields are private and final. Getters (but not setters) are generated (https://projectlombok.org/features/Value.html)
public class User {
String username;
String salt;
String hashedPassword;
}

Wyświetl plik

@ -0,0 +1,30 @@
package app.user;
import org.mindrot.jbcrypt.*;
import static app.Application.userDao;
public class UserController {
// Authenticate the user by hashing the inputted password using the stored salt,
// then comparing the generated hashed password to the stored hashed password
public static boolean authenticate(String username, String password) {
if (username.isEmpty() || password.isEmpty()) {
return false;
}
User user = userDao.getUserByUsername(username);
if (user == null) {
return false;
}
String hashedPassword = BCrypt.hashpw(password, user.getSalt());
return hashedPassword.equals(user.getHashedPassword());
}
// This method doesn't do anything, it's just included as an example
public static void setPassword(String username, String oldPassword, String newPassword) {
if (authenticate(username, oldPassword)) {
String newSalt = BCrypt.gensalt();
String newHashedPassword = BCrypt.hashpw(newSalt, newPassword);
// Update the user salt and password
}
}
}

Wyświetl plik

@ -0,0 +1,24 @@
package app.user;
import com.google.common.collect.*;
import java.util.*;
import java.util.stream.*;
public class UserDao {
private final List<User> users = ImmutableList.of(
// Username Salt for hash Hashed password (the password is "password" for all users)
new User("perwendel", "$2a$10$h.dl5J86rGH7I8bD9bZeZe", "$2a$10$h.dl5J86rGH7I8bD9bZeZeci0pDt0.VwFTGujlnEaZXPf/q7vM5wO"),
new User("davidase", "$2a$10$e0MYzXyjpJS7Pd0RVvHwHe", "$2a$10$e0MYzXyjpJS7Pd0RVvHwHe1HlCS4bZJ18JuywdEMLT83E1KDmUhCy"),
new User("federico", "$2a$10$E3DgchtVry3qlYlzJCsyxe", "$2a$10$E3DgchtVry3qlYlzJCsyxeSK0fftK4v0ynetVCuDdxGVl1obL.ln2")
);
public User getUserByUsername(String username) {
return users.stream().filter(b -> b.getUsername().equals(username)).findFirst().orElse(null);
}
public Iterable<String> getAllUserNames() {
return users.stream().map(User::getUsername).collect(Collectors.toList());
}
}

Wyświetl plik

@ -0,0 +1,30 @@
package app.util;
import spark.*;
import static app.util.RequestUtil.*;
public class Filters {
// If a user manually manipulates paths and forgets to add
// a trailing slash, redirect the user to the correct path
public static Filter addTrailingSlashes = (Request request, Response response) -> {
if (!request.pathInfo().endsWith("/")) {
response.redirect(request.pathInfo() + "/");
}
};
// Locale change can be initiated from any page
// The locale is extracted from the request and saved to the user's session
public static Filter handleLocaleChange = (Request request, Response response) -> {
if (getQueryLocale(request) != null) {
request.session().attribute("locale", getQueryLocale(request));
response.redirect(request.pathInfo());
}
};
// Enable GZIP for all responses
public static Filter addGzipHeader = (Request request, Response response) -> {
response.header("Content-Encoding", "gzip");
};
}

Wyświetl plik

@ -0,0 +1,18 @@
package app.util;
import com.fasterxml.jackson.databind.*;
import java.io.*;
public class JsonUtil {
public static String dataToJson(Object data) {
try {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
StringWriter sw = new StringWriter();
mapper.writeValue(sw, data);
return sw.toString();
} catch (IOException e) {
throw new RuntimeException("IOEXception while mapping object (" + data + ") to JSON");
}
}
}

Wyświetl plik

@ -0,0 +1,23 @@
package app.util;
import java.text.*;
import java.util.*;
public class MessageBundle {
private ResourceBundle messages;
public MessageBundle(String languageTag) {
Locale locale = languageTag != null ? new Locale(languageTag) : Locale.ENGLISH;
this.messages = ResourceBundle.getBundle("localization/messages", locale);
}
public String get(String message) {
return messages.getString(message);
}
public final String get(final String key, final Object... args) {
return MessageFormat.format(get(key), args);
}
}

Wyświetl plik

@ -0,0 +1,25 @@
package app.util;
import lombok.*;
public class Path {
// The @Getter methods are needed in order to access
// the variables from Velocity Templates
public static class Web {
@Getter public static final String INDEX = "/index/";
@Getter public static final String LOGIN = "/login/";
@Getter public static final String LOGOUT = "/logout/";
@Getter public static final String BOOKS = "/books/";
@Getter public static final String ONE_BOOK = "/books/:isbn/";
}
public static class Template {
public final static String INDEX = "/velocity/index/index.vm";
public final static String LOGIN = "/velocity/login/login.vm";
public final static String BOOKS_ALL = "/velocity/book/all.vm";
public static final String BOOKS_ONE = "/velocity/book/one.vm";
public static final String NOT_FOUND = "/velocity/notFound.vm";
}
}

Wyświetl plik

@ -0,0 +1,57 @@
package app.util;
import spark.*;
public class RequestUtil {
public static String getQueryLocale(Request request) {
return request.queryParams("locale");
}
public static String getParamIsbn(Request request) {
return request.params("isbn");
}
public static String getQueryUsername(Request request) {
return request.queryParams("username");
}
public static String getQueryPassword(Request request) {
return request.queryParams("password");
}
public static String getQueryLoginRedirect(Request request) {
return request.queryParams("loginRedirect");
}
public static String getSessionLocale(Request request) {
return request.session().attribute("locale");
}
public static String getSessionCurrentUser(Request request) {
return request.session().attribute("currentUser");
}
public static boolean removeSessionAttrLoggedOut(Request request) {
Object loggedOut = request.session().attribute("loggedOut");
request.session().removeAttribute("loggedOut");
return loggedOut != null;
}
public static String removeSessionAttrLoginRedirect(Request request) {
String loginRedirect = request.session().attribute("loginRedirect");
request.session().removeAttribute("loginRedirect");
return loginRedirect;
}
public static boolean clientAcceptsHtml(Request request) {
String accept = request.headers("Accept");
return accept != null && accept.contains("text/html");
}
public static boolean clientAcceptsJson(Request request) {
String accept = request.headers("Accept");
return accept != null && accept.contains("application/json");
}
}

Wyświetl plik

@ -0,0 +1,39 @@
package app.util;
import org.apache.velocity.app.*;
import org.eclipse.jetty.http.*;
import spark.*;
import spark.template.velocity.*;
import java.util.*;
import static app.util.RequestUtil.*;
public class ViewUtil {
// Renders a template given a model and a request
// The request is needed to check the user session for language settings
// and to see if the user is logged in
public static String render(Request request, Map<String, Object> model, String templatePath) {
model.put("msg", new MessageBundle(getSessionLocale(request)));
model.put("currentUser", getSessionCurrentUser(request));
model.put("WebPath", Path.Web.class); // Access application URLs from templates
return strictVelocityEngine().render(new ModelAndView(model, templatePath));
}
public static Route notAcceptable = (Request request, Response response) -> {
response.status(HttpStatus.NOT_ACCEPTABLE_406);
return new MessageBundle(getSessionLocale(request)).get("ERROR_406_NOT_ACCEPTABLE");
};
public static Route notFound = (Request request, Response response) -> {
response.status(HttpStatus.NOT_FOUND_404);
return render(request, new HashMap<>(), Path.Template.NOT_FOUND);
};
private static VelocityTemplateEngine strictVelocityEngine() {
VelocityEngine configuredEngine = new VelocityEngine();
configuredEngine.setProperty("runtime.references.strict", true);
configuredEngine.setProperty("resource.loader", "class");
configuredEngine.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
return new VelocityTemplateEngine(configuredEngine);
}
}

Wyświetl plik

@ -0,0 +1,33 @@
## Common
COMMON_TITLE=Ze Spark Library
COMMON_FOOTER_TEXT=Ze application uses <a href="https://openlibrary.org/" target="_blank">OpenLibrary</a> vor images.
COMMON_NAV_ALLBOOKS=View all ze books
COMMON_NAV_LOGIN=Innlogg
COMMON_NAV_LOGOUT=Auslogg
ERROR_406_NOT_ACCEPTABLE=No zuitable content found. Please zpecify eizer 'html/text' or 'application/json'.
ERROR_404_NOT_FOUND=Ve cannot find ze page you are looking for <small>(error 404)</small>
## Index
INDEX_HEADING=Velcome to ze Spark Library
INDEX_REGISTERED_USERS=Zere are currently {0} users registered:
INDEX_PASSWORD_INFO=It seems zey have all chosen ze password "password" for some reason. Hov silly.
INDEX_BOOK_OF_THE_DAY_TEXT=Ze book of ze day is:
INDEX_BOOK_OF_THE_DAY_LINK=<strong>{0} von {1}</strong>
## Login
LOGIN_HEADING=Innlogg
LOGIN_INSTRUCTIONS=Please enter dein username und password. <small><br>(Zee ze <a href="{0}">index page</a> if you need a hint)</small>
LOGIN_AUTH_SUCCEEDED=You''re logged in as ''{0}''.
LOGIN_AUTH_FAILED=Ze login informazion you zuplied vas incorrect.
LOGIN_LOGGED_OUT=You have been logged aus.
LOGIN_LABEL_USERNAME=Username
LOGIN_LABEL_PASSWORD=Password
LOGIN_BUTTON_LOGIN=Innlogg
## Books
BOOKS_HEADING_ALL=All ze books
BOOKS_CAPTION=<strong>{0}</strong> <br>von {1}
BOOKS_BOOK_NOT_FOUND=Book nicht found

Wyświetl plik

@ -0,0 +1,33 @@
## Common
COMMON_TITLE=Spark Library
COMMON_FOOTER_TEXT=This Application uses <a href="https://openlibrary.org/" target="_blank">OpenLibrary</a> for images.
COMMON_NAV_ALLBOOKS=View all books
COMMON_NAV_LOGIN=Log in
COMMON_NAV_LOGOUT=Log out
ERROR_406_NOT_ACCEPTABLE=No suitable content found. Please specify either 'html/text' or 'application/json'.
ERROR_404_NOT_FOUND=We can't find the page you're looking for <small>(error 404)</small>
## Index
INDEX_HEADING=Welcome to the Spark Library
INDEX_REGISTERED_USERS=There are currently {0} users registered:
INDEX_PASSWORD_INFO=It seems they've all chosen the password "password" for some reason. How silly.
INDEX_BOOK_OF_THE_DAY_TEXT=The book of the day is:
INDEX_BOOK_OF_THE_DAY_LINK=<strong>{0} by {1}</strong>
## Login
LOGIN_HEADING=Login
LOGIN_INSTRUCTIONS=Please enter your username and password. <small><br>(See the <a href="{0}">index page</a> if you need a hint)</small>
LOGIN_AUTH_SUCCEEDED=You''re logged in as ''{0}''.
LOGIN_AUTH_FAILED=The login information you supplied was incorrect.
LOGIN_LOGGED_OUT=You have been logged out.
LOGIN_LABEL_USERNAME=Username
LOGIN_LABEL_PASSWORD=Password
LOGIN_BUTTON_LOGIN=Log in
## Books
BOOKS_HEADING_ALL=All books
BOOKS_CAPTION=<strong>{0}</strong> <br>by {1}
BOOKS_BOOK_NOT_FOUND=Book not found

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 55 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.0 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 51 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.4 KiB

Wyświetl plik

@ -0,0 +1,288 @@
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
font-family: Tahoma, Arial, sans-serif;
position: relative;
min-height: 100%;
}
body {
padding: 0 0 40px;
color: #333;
background: #f9f9f9;
}
h1, h2, h3, h4 {
font-family: monospace;
font-weight: 300;
color: #444;
}
small {
color: #555;
}
header {
background: #274555;
border-bottom: 5px solid #ff7761;
box-shadow: 0 1px 0 0 rgba(0,0,0,.6);
}
nav {
padding: 15px;
margin: 0 auto;
max-width: 800px;
position: relative;
}
nav #menu {
margin-top: 20px;
float: right;
}
a {
text-decoration: none;
color: #ff7761;
}
#menu li {
float: left;
margin: 0 10px;
}
#menu li a, #logout {
background: transparent;
cursor: pointer;
border: 0;
font-size: 16px;
display: inline-block;
color: #fff;
text-align: center;
height: 30px;
line-height: 30px;
padding: 0 10px;
text-decoration: none;
}
#logo {
max-height: 50px;
}
#chooseLanguage {
top: 15px;
right: 35px;
position: absolute;
}
#chooseLanguage li {
float: left;
}
#chooseLanguage button {
cursor: pointer;
margin-left: 8px;
width: 18px;
height: 18px;
border-radius: 9px;
opacity: 0.6;
border: 1px solid #222;
background-size: 100%;
}
#chooseLanguage button:hover {
opacity: 0.8;
}
main {
max-width: 800px;
margin: 0 auto;
padding: 15px;
}
#content {
padding: 15px;
background: #fff;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25);
}
footer {
position: absolute;
left: 0;
bottom: 0;
height: 40px;
line-height: 40px;
width: 100%;
text-align: center;
background: #fff;
border-top: 1px solid #f9a11b;
font-size: 14px;
}
nav ul, nav li {
margin: 0;
padding: 0;
list-style-type: none;
}
/* Needlessly fancy menu hover effect */
#menu li {
position: relative;
}
#menu li a::after, #logout:after {
position: absolute;
top: 28px;
left: 0;
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.5);
border-radius: 5px;
content: '';
opacity: 0;
-webkit-transition: opacity 0.3s, -webkit-transform 0.3s;
transition: opacity 0.3s, transform 0.3s;
-webkit-transform: translateY(10px);
transform: translateY(10px);
}
#logout:hover::after,
#logout:focus::after,
#menu li a:hover::after,
#menu li a:focus::after {
opacity: 1;
-webkit-transform: translateY(0px);
transform: translateY(0px);
}
/* Very basic grid */
.row {
width: 100%;
overflow: auto;
}
.row > * {
float: left;
}
.row-2 .col {
width: 49%
}
.row-2 .col:nth-child(odd) {
margin: 0% 1% 0% 0%
}
.row-2 .col:nth-child(even) {
margin: 0% 0% 0% 1%
}
.row-3 .col {
width: 32%
}
.row-3 .col:nth-child(3n+1) {
margin: 0% 1% 0% 0%
}
.row-3 .col:nth-child(3n+2) {
margin: 0% 1% 0% 1%
}
.row-3 .col:nth-child(3n+3) {
margin: 0% 0% 0% 1%
}
@media screen and (max-width: 550px) {
.row .col:nth-child(n) {
width: 100%;
margin-right: 0;
margin-left: 0;
}
}
.col img {
display: block;
width: 100%;
}
/* Book related stuff */
a.book {
display: block;
text-align: center;
text-decoration: none;
color: #333;
padding: 10px;
border-radius: 5px;
}
a.book:hover {
background: #e2e9f5;
}
a .bookCover {
padding: 10px;
display: flex;
align-items: center;
justify-content: center;
}
a .bookCover img {
border-radius: 5px;
min-height: 200px;
max-height: 200px;
width: auto;
}
.bookCover img {
margin-top: 20px;
border-radius: 10px;
width: 100%;
}
/* Login Form */
#loginForm {
max-width: 400px;
margin: 0 auto;
}
#loginForm label {
display: block;
width: 100%
}
#loginForm input {
border: 1px solid #ddd;
padding: 8px 12px;
width: 100%;
border-radius: 3px;
margin: 2px 0 20px 0;
}
#loginForm input[type="submit"] {
color: white;
background: #274555;
border: 0;
cursor: pointer;
}
.notification {
padding: 10px;
background: #333;
color: white;
border-radius: 3px;
}
.good.notification {
background: #008900;
}
.bad.notification {
background: #bb0000;
}

Wyświetl plik

@ -0,0 +1,16 @@
#parse("/velocity/layout.vm")
#@mainLayout()
<h1>$msg.get("BOOKS_HEADING_ALL")</h1>
<div class="row row-3">
#foreach($book in $books)
<div class="col">
<a class="book" href="$WebPath.getBOOKS()$book.getIsbn()">
<div class="bookCover">
<img src="$book.getMediumCover()" alt="$book.getTitle()">
</div>
$msg.get("BOOKS_CAPTION", $book.getTitle(), $book.getAuthor())
</a>
</div>
#end
</div>
#end

Wyświetl plik

@ -0,0 +1,14 @@
#parse("/velocity/layout.vm")
#@mainLayout()
#if($book)
<h1>$book.getTitle()</h1>
<h2>$book.getAuthor()</h2>
<div class="book">
<div class="bookCover">
<img src="$book.getLargeCover()" alt="$book.getTitle()">
</div>
</div>
#else
<h1>$msg.get("BOOKS_BOOK_NOT_FOUND")</h1>
#end
#end

Wyświetl plik

@ -0,0 +1,22 @@
#parse("/velocity/layout.vm")
#@mainLayout()
<h1>$msg.get("INDEX_HEADING")</h1>
<h2>$msg.get("INDEX_REGISTERED_USERS", $users.size())</h2>
<ul>
#foreach($user in $users)
<li>$user</li>
#end
</ul>
<p>$msg.get("INDEX_PASSWORD_INFO")</p>
#if($book)
<h2>$msg.get("INDEX_BOOK_OF_THE_DAY_TEXT")</h2>
<div class="row row-2">
<div class="col">
<a href="$WebPath.getBOOKS()$book.getIsbn()">$msg.get("INDEX_BOOK_OF_THE_DAY_LINK", $book.getTitle(), $book.getAuthor())</a>
</div>
<div class="col">
<img src="$book.getLargeCover()" alt="$book.getTitle()">
</div>
</div>
#end
#end

Wyświetl plik

@ -0,0 +1,47 @@
#macro(mainLayout)
<html>
<head>
<title>$msg.get("COMMON_TITLE")</title>
<link rel="stylesheet" href="/main.css">
<link rel="icon" href="/favicon.png">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<header>
<nav>
<a href="$WebPath.getINDEX()"><img id="logo" src="/img/logo.png" alt="Spark Library"></a>
<ul id="chooseLanguage">
<form>
<li>
<button name="locale" value="de" style="background-image: url(/img/german.png);"></button>
</li>
<li>
<button name="locale" value="en" style="background-image: url(/img/english.png);"></button>
</li>
</form>
</ul>
<ul id="menu">
<li><a href="$WebPath.getBOOKS()">$msg.get("COMMON_NAV_ALLBOOKS")</a></li>
#if($currentUser)
<li>
<form method="post" action="$WebPath.getLOGOUT()">
<button id="logout">$msg.get("COMMON_NAV_LOGOUT")</button>
</form>
</li>
#else
<li><a href="$WebPath.getLOGIN()"">$msg.get("COMMON_NAV_LOGIN")</a></li>
#end
</ul>
</nav>
</header>
<main>
<div id="content">
$bodyContent
</div>
</main>
<footer>
$msg.get("COMMON_FOOTER_TEXT")
</footer>
</body>
</html>
#end

Wyświetl plik

@ -0,0 +1,22 @@
#parse("/velocity/layout.vm")
#@mainLayout()
<form id="loginForm" method="post">
#if($authenticationFailed)
<p class="bad notification">$msg.get("LOGIN_AUTH_FAILED")</p>
#elseif($authenticationSucceeded)
<p class="good notification">$msg.get("LOGIN_AUTH_SUCCEEDED", $currentUser)</p>
#elseif($loggedOut)
<p class="notification">$msg.get("LOGIN_LOGGED_OUT")</p>
#end
<h1>$msg.get("LOGIN_HEADING")</h1>
<p>$msg.get("LOGIN_INSTRUCTIONS", $WebPath.getINDEX())</p>
<label>$msg.get("LOGIN_LABEL_USERNAME")</label>
<input type="text" name="username" placeholder="$msg.get("LOGIN_LABEL_USERNAME")" value="" required>
<label>$msg.get("LOGIN_LABEL_PASSWORD")</label>
<input type="password" name="password" placeholder="$msg.get("LOGIN_LABEL_PASSWORD")" value="" required>
#if($loginRedirect)
<input type="hidden" name="loginRedirect" value="$loginRedirect">
#end
<input type="submit" value="$msg.get("LOGIN_BUTTON_LOGIN")">
</form>
#end

Wyświetl plik

@ -0,0 +1,4 @@
#parse("/velocity/layout.vm")
#@mainLayout()
<h1>$msg.get("ERROR_404_NOT_FOUND")</h1>
#end

Wyświetl plik

@ -0,0 +1,11 @@
#* @implicitly included *#
#* @vtlvariable name="WebPath" type="app.util.Path.Web" *#
#* @vtlvariable name="msg" type="app.util.MessageBundle" *#
#* @vtlvariable name="books" type="java.lang.Iterable<app.book.Book>" *#
#* @vtlvariable name="book" type="app.book.Book" *#
#* @vtlvariable name="users" type="java.lang.Iterable<app.user.User>" *#
#* @vtlvariable name="currentUser" type="java.lang.String" *#
#* @vtlvariable name="loggedOut" type="java.lang.String" *#
#* @vtlvariable name="authenticationFailed" type="java.lang.String" *#
#* @vtlvariable name="authenticationSucceeded" type="java.lang.String" *#
#* @vtlvariable name="loginRedirect" type="java.lang.String" *#

90
piku.py
Wyświetl plik

@ -30,6 +30,7 @@ from time import sleep
from urllib.request import urlopen
from pwd import getpwuid
from grp import getgrgid
from yaml import safe_load
# === Make sure we can access all system binaries ===
@ -244,7 +245,7 @@ def parse_procfile(filename):
if not len(workers):
return {}
# WSGI trumps regular web workers
if 'wsgi' in workers:
if 'wsgi' or 'jwsgi' in workers:
if 'web' in workers:
echo("Warning: found both 'wsgi' and 'web' workers, disabling 'web'", fg='yellow')
del(workers['web'])
@ -332,6 +333,9 @@ def do_deploy(app, deltas={}, newrev=None):
elif exists(join(app_path, 'pom.xml')) and check_requirements(['java', 'mvn']):
echo("-----> Java app detected.", fg='green')
settings.update(deploy_java(app, deltas))
elif exists(join(app_path, 'build.gradle')) and check_requirements(['java', 'gradle']):
echo("-----> Gradle Java app detected.", fg='green')
settings.update(deploy_java(app, deltas))
elif (exists(join(app_path, 'Godeps')) or len(glob(join(app_path,'*.go')))) and check_requirements(['go']):
echo("-----> Go app detected.", fg='green')
settings.update(deploy_go(app, deltas))
@ -350,10 +354,78 @@ def do_deploy(app, deltas={}, newrev=None):
else:
echo("Error: app '{}' not found.".format(app), fg='red')
def deploy_gradle(app, deltas={}):
"""Deploy a Java application using Gradle"""
virtual = join(ENV_ROOT, app)
target_path = join(APP_ROOT, app, 'build')
env_file = join(APP_ROOT, app, 'ENV')
build = join(APP_ROOT, app, 'build.gradle')
first_time = False
if not exists(target_path) or first_time == True:
venv = 'mkdir ' + virtual
call(venv, cwd=PIKU_ROOT, shell=True)
env = {
'VIRTUAL_ENV': virtual,
"PATH": ':'.join([join(virtual, "bin"), join(app, ".bin"),environ['PATH']])
}
if exists(env_file):
env.update(parse_settings(env_file, env))
echo("-----> Building Java Application")
call('gradle build', cwd=join(APP_ROOT, app), env=env, shell=True)
first_time = True
else:
if getmtime(build) > getmtime(target_path):
echo ("-----> Performing a clean build")
call('gradle clean build', cwd=join(APP_ROOT, app), env=env, shell=True)
return spawn_app(app, deltas)
def deploy_java(app, deltas={}):
"""Deploy a Java application"""
raise NotImplementedError
"""Deploy a Java application using Maven"""
# Check for if pom.xml exists or build.gradle
# Since gradle can build a variety of projects from scala, clojure etc, I think it is better to add a deploy_gradle function
# TODO: Use jenv to isolate Java Application environments
virtual = join(ENV_ROOT, app)
target_path = join(APP_ROOT, app, 'target')
env_file = join(APP_ROOT, app, 'ENV')
pom = join(APP_ROOT, app, 'pom.xml')
first_time = False
if not exists(target_path) or first_time == True:
venv = 'mkdir ' + virtual
call(venv, cwd=PIKU_ROOT, shell=True)
env = {
'VIRTUAL_ENV': virtual,
"PATH": ':'.join([join(virtual, "bin"), join(app, ".bin"),environ['PATH']])
}
if exists(env_file):
env.update(parse_settings(env_file, env))
echo("-----> Building Java Application")
call('mvn compile', cwd=join(APP_ROOT, app), env=env, shell=True)
# Compiles your java project according to pom.xml
echo("-----> Running Maven Tests")
call('mvn test', cwd=join(APP_ROOT, app), env=env, shell=True)
echo("-----> Tests Completed \n-----> Packaging Compiled Sources ")
call('mvn package', cwd=join(APP_ROOT, app), env=env, shell=True)
echo("-----> Finished Packaging && Now Verifying Compiled Packages")
call('mvn verify', cwd=join(APP_ROOT, app), env=env, shell=True)
echo("-----> Installing Application on local repository for future usage")
call('mvn install', cwd=join(APP_ROOT, app), env=env, shell=True)
echo('----->Successfully deployed your package on Maven Local Repository')
echo('-----> Deploying App to Maven Central Repository')
first_time = True
else:
if getmtime(pom) > getmtime(target_path):
echo("-----> Destroying previous builds")
call('mvn clean', cwd=join(APP_ROOT, app), env=env, shell=True)
echo('-----> Starting new build')
call('mvn compile && mvn test && mvn package && mvn verify && mvn install && mvn deploy', cwd=join(APP_ROOT, app), env=env, shell=True)
return spawn_app(app, deltas)
def deploy_go(app, deltas={}):
@ -515,7 +587,7 @@ def spawn_app(app, deltas={}):
if exists(settings):
env.update(parse_settings(settings, env))
if 'web' in workers or 'wsgi' in workers:
if 'web' in workers or 'wsgi' or 'jwsgi' in workers:
# Pick a port if none defined
if 'PORT' not in env:
env['PORT'] = str(get_free_port())
@ -545,7 +617,7 @@ def spawn_app(app, deltas={}):
# default to reverse proxying to the TCP port we picked
env['INTERNAL_NGINX_UWSGI_SETTINGS'] = 'proxy_pass http://{BIND_ADDRESS:s}:{PORT:s};'.format(**env)
if 'wsgi' in workers:
if 'wsgi' or 'jwsgi' in workers:
sock = join(NGINX_ROOT, "{}.sock".format(app))
env['INTERNAL_NGINX_UWSGI_SETTINGS'] = expandvars(INTERNAL_NGINX_UWSGI_SETTINGS, env)
env['NGINX_SOCKET'] = env['BIND_ADDRESS'] = "unix://" + sock
@ -720,6 +792,14 @@ def spawn_worker(app, kind, command, env, ordinal=1):
if exists(join(env_path, "bin", "activate_this.py")):
settings.append(('virtualenv', env_path))
if kind== 'jwsgi':
settings.extend([
('module', command),
('threads', env.get('UWSGI_THREADS','4')),
('plugin', 'jvm'),
('plugin', 'jwsgi')
])
python_version = int(env.get('PYTHON_VERSION','3'))
if kind == 'wsgi':