prep to push
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
arguments=
|
arguments=
|
||||||
auto.sync=false
|
auto.sync=false
|
||||||
build.scans.enabled=false
|
build.scans.enabled=false
|
||||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(LOCAL_INSTALLATION(C\:\\ProgramData\\chocolatey\\lib\\gradle\\tools\\gradle-7.0))
|
connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(7.0))
|
||||||
connection.project.dir=
|
connection.project.dir=
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
gradle.user.home=
|
gradle.user.home=
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
As used herein, “this License” refers to version 3 of the GNU Lesser General Public License, and the “GNU GPL” refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
“The Library” refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An “Application” is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A “Combined Work” is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the “Linked Version”.
|
||||||
|
|
||||||
|
The “Minimal Corresponding Source” for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The “Corresponding Application Code” for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy.
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License.
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license document.
|
||||||
|
4. Combined Works.
|
||||||
|
You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license document.
|
||||||
|
c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.
|
||||||
|
d) Do one of the following:
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.
|
||||||
|
1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.
|
||||||
|
e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)
|
||||||
|
5. Combined Libraries.
|
||||||
|
You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License.
|
||||||
|
b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.
|
||||||
@@ -1,15 +1,22 @@
|
|||||||
# Jabba the easy going java web app plumber
|
# Jabba the easy going java web app plumber
|
||||||
Jabba is a java library that gets its inspiration from Pythong flask. It will expose all the elementary features needed for deveopment of web apps and microservices.
|
Jabba is a java library that gets its inspiration from Python Flask. It will expose all the elementary features needed for deveopment of web apps and microservices.
|
||||||
|
|
||||||
# How to Run Things
|
# How to Run Things
|
||||||
* running a build via: gradle jar
|
* running a build via: gradle jar
|
||||||
* running a test via: gradle test
|
* running a test via: gradle test
|
||||||
* running a continouse server via: gradle runServer, then work on code
|
* running a continouse server via: gradle --watch-fs -t runServer, then work on code
|
||||||
|
|
||||||
## Things Left to Do
|
## Things Left to Do
|
||||||
* ~~Complete support for demarshalling and marshalling of objects to java methods~~ on 10/4/2021
|
* ~~Complete support for demarshalling and marshalling of objects to java methods~~
|
||||||
* ~~Session middleware~~
|
* ~~Session middleware~~
|
||||||
* Auth middleware supporting basic and digest, an security entities
|
* ~~Auth middleware supporting basic and digest, and security entities~~
|
||||||
* ~~Static file serving~~
|
* ~~Static file serving~~
|
||||||
* ~~Templating like jinja~~
|
* ~~Templating like jinja~~
|
||||||
* Database layer or serial/deserial system like SQL Alchemy
|
* ~~Homepage with login templates~~
|
||||||
|
* ~~Error page~~
|
||||||
|
* ~~Menu handling~~
|
||||||
|
* ~~Database layer or serial/deserial system like SQL Alchemy~~
|
||||||
|
|
||||||
|
With above things complete we ~~will~~ have a library that can be used for new webapps.
|
||||||
|
Don't have a profile page (which goes into app templates) and the dbo layer is basic not like sql alchemy but mostly things are in place. At this point we could use jabba to spawn new apps.
|
||||||
|
Now I could prepare it for github and for maven central.
|
||||||
+26
-13
@@ -11,8 +11,11 @@ apply plugin: 'application'
|
|||||||
apply plugin: 'maven-publish'
|
apply plugin: 'maven-publish'
|
||||||
|
|
||||||
group='com.reliancy'
|
group='com.reliancy'
|
||||||
|
mainClassName = group+'.'+name+'.JettyApp'
|
||||||
version = '0.1'
|
version = '0.1'
|
||||||
mainClassName = group+'.'+name+'.Router'
|
sourceCompatibility = 1.8
|
||||||
|
targetCompatibility = 1.8
|
||||||
|
|
||||||
System.out.println("group:"+group);
|
System.out.println("group:"+group);
|
||||||
System.out.println("name:"+name);
|
System.out.println("name:"+name);
|
||||||
System.out.println("version:"+version);
|
System.out.println("version:"+version);
|
||||||
@@ -22,17 +25,34 @@ repositories {
|
|||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
sourceCompatibility = 1.8
|
publishing {
|
||||||
targetCompatibility = 1.8
|
publications {
|
||||||
|
mavenJava(MavenPublication) {
|
||||||
|
from components.java
|
||||||
|
pom {
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = 'GNU Lesser General Public License, Version 3.0'
|
||||||
|
url = 'https://www.gnu.org/licenses/lgpl-3.0.txt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
javadoc {
|
||||||
|
source = sourceSets.main.allJava
|
||||||
|
//classpath = configurations.compile
|
||||||
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.eclipse.jetty:jetty-server:11.0.1"
|
implementation "org.eclipse.jetty:jetty-server:11.0.1"
|
||||||
implementation "org.slf4j:slf4j-simple:2.0.0-alpha0"
|
implementation "org.slf4j:slf4j-simple:2.0.0-alpha0"
|
||||||
//implementation 'com.hubspot.jinjava:jinjava:2.5.10'
|
//implementation 'com.hubspot.jinjava:jinjava:2.5.10'
|
||||||
implementation 'com.github.jknack:handlebars:4.2.1'
|
implementation 'com.github.jknack:handlebars:4.3.0'
|
||||||
implementation 'com.h2database:h2:1.4.200'
|
implementation 'com.h2database:h2:2.1.214'
|
||||||
// https://mvnrepository.com/artifact/org.postgresql/postgresql
|
// https://mvnrepository.com/artifact/org.postgresql/postgresql
|
||||||
implementation 'org.postgresql:postgresql:42.3.1'
|
implementation 'org.postgresql:postgresql:42.5.0'
|
||||||
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
|
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
|
||||||
implementation 'com.zaxxer:HikariCP:5.0.0'
|
implementation 'com.zaxxer:HikariCP:5.0.0'
|
||||||
|
|
||||||
@@ -180,13 +200,6 @@ task runServer{
|
|||||||
//args "arg1", "arg2"
|
//args "arg1", "arg2"
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
mavenJava(MavenPublication) {
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eclipse{
|
eclipse{
|
||||||
classpath {
|
classpath {
|
||||||
defaultOutputDir = file("build") ///default
|
defaultOutputDir = file("build") ///default
|
||||||
|
|||||||
@@ -1,145 +1,57 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
<!-- This module was also published with a richer model, Gradle metadata, -->
|
||||||
|
<!-- which should be used instead. Do not delete the following line which -->
|
||||||
|
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
|
||||||
|
<!-- that they should prefer consuming it instead. -->
|
||||||
|
<!-- do_not_remove: published-with-gradle-metadata -->
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.reliancy</groupId>
|
||||||
<groupId>com.reliancy.jabba</groupId>
|
|
||||||
<artifactId>jabba</artifactId>
|
<artifactId>jabba</artifactId>
|
||||||
<version>1.0</version>
|
<version>0.1</version>
|
||||||
<packaging>jar</packaging>
|
<licenses>
|
||||||
<name>jabba</name>
|
<license>
|
||||||
<url>http://www.reliancy.com</url>
|
<name>The Apache License, Version 2.0</name>
|
||||||
|
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||||
<properties>
|
</license>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
</licenses>
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<version>4.11</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-server</artifactId>
|
<artifactId>jetty-server</artifactId>
|
||||||
<version>11.0.7</version>
|
<version>12.0.0.alpha1</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>jetty-servlet</artifactId>
|
<artifactId>slf4j-simple</artifactId>
|
||||||
<version>11.0.1</version>
|
<version>2.0.0-alpha0</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.hubspot.jinjava</groupId>
|
<groupId>com.github.jknack</groupId>
|
||||||
<artifactId>jinjava</artifactId>
|
<artifactId>handlebars</artifactId>
|
||||||
<version>2.5.10</version>
|
<version>4.3.0</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<version>2.1.214</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
<version>42.5.0</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.zaxxer</groupId>
|
||||||
|
<artifactId>HikariCP</artifactId>
|
||||||
|
<version>5.0.0</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<version>3.1.2</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>copy-dependencies</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>copy-dependencies</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<!-- Maven Assembly Plugin -->
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
|
||||||
<version>2.4.1</version>
|
|
||||||
<configuration>
|
|
||||||
<!-- get all project dependencies -->
|
|
||||||
<descriptorRefs>
|
|
||||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
|
||||||
</descriptorRefs>
|
|
||||||
<!-- MainClass in mainfest make a executable jar -->
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>com.reliancy.jabba.Router</mainClass>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
|
|
||||||
</configuration>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>make-assembly</id>
|
|
||||||
<!-- bind to the packaging phase -->
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>single</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>exec-maven-plugin</artifactId>
|
|
||||||
<version>3.0.0</version>
|
|
||||||
<configuration>
|
|
||||||
<mainClass>com.reliancy.jabba.Router</mainClass>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-clean-plugin</artifactId>
|
|
||||||
<version>3.1.0</version>
|
|
||||||
</plugin>
|
|
||||||
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
|
||||||
<version>3.0.2</version>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<version>3.8.0</version>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<version>2.22.1</version>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
|
||||||
<version>3.0.2</version>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-install-plugin</artifactId>
|
|
||||||
<version>2.5.2</version>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-deploy-plugin</artifactId>
|
|
||||||
<version>2.8.2</version>
|
|
||||||
</plugin>
|
|
||||||
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-site-plugin</artifactId>
|
|
||||||
<version>3.7.1</version>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
|
||||||
<version>3.0.0</version>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</pluginManagement>
|
|
||||||
</build>
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Observable;
|
||||||
|
|
||||||
|
/** A more or less virtual collection of items.
|
||||||
|
* this object is a suitable holder of resultsets. it will be overridable so
|
||||||
|
* we can create specialized virtual holders that use backends. by itself it will
|
||||||
|
* implement in memory list.
|
||||||
|
* also this class is an observable and we can monitor update to it.
|
||||||
|
*/
|
||||||
|
public class Bag<E> extends Observable implements Collection<E>{
|
||||||
|
/** event to send to observers. */
|
||||||
|
public static final class BagChanged<E>{
|
||||||
|
public static final int ADD=0;
|
||||||
|
public static final int REMOVE=1;
|
||||||
|
public static final int ACCESS=2;
|
||||||
|
public static final int POST_LOAD=3;
|
||||||
|
public static final int PRE_SAVE=4;
|
||||||
|
final Bag<E> bag;
|
||||||
|
final int operation;
|
||||||
|
final Object[] arguments;
|
||||||
|
public BagChanged(Bag<E> p,int op,Object ... args){
|
||||||
|
bag=p;
|
||||||
|
operation=op;
|
||||||
|
arguments=args;
|
||||||
|
}
|
||||||
|
public Bag<E> getBag() {
|
||||||
|
return bag;
|
||||||
|
}
|
||||||
|
public int getOperation() {
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
public Object[] getArguments() {
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final ArrayList<E> items=new ArrayList<>();
|
||||||
|
|
||||||
|
public Bag(){
|
||||||
|
}
|
||||||
|
public Bag(Iterable<E> o){
|
||||||
|
this(o.iterator());
|
||||||
|
}
|
||||||
|
public Bag(Iterator<E> o){
|
||||||
|
while(o.hasNext()) add(o.next());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return size()==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
final Iterator<E> it=iterator();
|
||||||
|
while(it.hasNext()){
|
||||||
|
final E e=it.next();
|
||||||
|
if(e!=null && o!=null && e.equals(o)) return true;
|
||||||
|
else if(e==o) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(Collection<?> c) {
|
||||||
|
for (Object e : c) if (!contains(e)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public ListIterator<E> listIterator(){
|
||||||
|
return listIterator(0);
|
||||||
|
}
|
||||||
|
public ListIterator<E> listIterator(int offset){
|
||||||
|
return items.listIterator(offset);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return items.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
return toArray(new Object[size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T[] toArray(T[] a) {
|
||||||
|
return items.toArray(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(E e) {
|
||||||
|
if(items.contains(e)) return true;
|
||||||
|
if(countObservers()>0){
|
||||||
|
BagChanged<E> evt=new Bag.BagChanged<>(this,BagChanged.ADD,e);
|
||||||
|
setChanged();
|
||||||
|
notifyObservers(evt);
|
||||||
|
}
|
||||||
|
return items.add(e);
|
||||||
|
}
|
||||||
|
public Bag<E> append(E e){
|
||||||
|
add(e);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
if(!contains(o)) return false;
|
||||||
|
if(countObservers()>0){
|
||||||
|
BagChanged<E> evt=new Bag.BagChanged<>(this,BagChanged.REMOVE,o);
|
||||||
|
setChanged();
|
||||||
|
notifyObservers(evt);
|
||||||
|
}
|
||||||
|
return items.remove(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends E> c) {
|
||||||
|
if(countObservers()>0){
|
||||||
|
BagChanged<E> evt=new Bag.BagChanged<>(this,BagChanged.ADD,c.toArray());
|
||||||
|
setChanged();
|
||||||
|
notifyObservers(evt);
|
||||||
|
}
|
||||||
|
if(c==null || c.size()==0) return false;
|
||||||
|
c.forEach(e->{this.append(e);});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> c) {
|
||||||
|
if(countObservers()>0){
|
||||||
|
BagChanged<E> evt=new Bag.BagChanged<>(this,BagChanged.REMOVE,c!=null?c.toArray():null);
|
||||||
|
setChanged();
|
||||||
|
notifyObservers(evt);
|
||||||
|
}
|
||||||
|
if(c!=null){
|
||||||
|
return items.removeAll(c);
|
||||||
|
}else{
|
||||||
|
items.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection<?> c) {
|
||||||
|
return items.retainAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
removeAll(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -9,78 +16,93 @@ public class Check implements Iterable<Check> {
|
|||||||
public static abstract class Op{
|
public static abstract class Op{
|
||||||
public abstract boolean met(Check c,Object val);
|
public abstract boolean met(Check c,Object val);
|
||||||
}
|
}
|
||||||
|
/** logical AND operation. */
|
||||||
public static Op AND=new Op(){
|
public static Op AND=new Op(){
|
||||||
public String toString(){return "AND";}
|
public String toString(){return "AND";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** logical OR operation. */
|
||||||
public static Op OR=new Op(){
|
public static Op OR=new Op(){
|
||||||
public String toString(){return "OR";}
|
public String toString(){return "OR";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** logical NOT operation. */
|
||||||
public static Op NOT=new Op(){
|
public static Op NOT=new Op(){
|
||||||
public String toString(){return "NOT";}
|
public String toString(){return "NOT";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** arithmetic equal test. */
|
||||||
public static Op EQ=new Op(){
|
public static Op EQ=new Op(){
|
||||||
public String toString(){return "=";}
|
public String toString(){return "=";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** arithmetic negated equal test. */
|
||||||
public static Op NEQ=new Op(){
|
public static Op NEQ=new Op(){
|
||||||
public String toString(){return "<>";}
|
public String toString(){return "<>";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** greater than check. */
|
||||||
public static Op GT=new Op(){
|
public static Op GT=new Op(){
|
||||||
public String toString(){return ">";}
|
public String toString(){return ">";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** greater than or equal check. */
|
||||||
public static Op GTE=new Op(){
|
public static Op GTE=new Op(){
|
||||||
public String toString(){return ">=";}
|
public String toString(){return ">=";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** less than check. */
|
||||||
public static Op LT=new Op(){
|
public static Op LT=new Op(){
|
||||||
public String toString(){return "<";}
|
public String toString(){return "<";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** less than or equal check. */
|
||||||
public static Op LTE=new Op(){
|
public static Op LTE=new Op(){
|
||||||
public String toString(){return "<=";}
|
public String toString(){return "<=";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** like check case insensitive. */
|
||||||
public static Op LIKE=new Op(){
|
public static Op LIKE=new Op(){
|
||||||
public String toString(){return "LIKE";}
|
public String toString(){return "LIKE";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** set membership check. */
|
||||||
public static Op IN=new Op(){
|
public static Op IN=new Op(){
|
||||||
public String toString(){return "IN";}
|
public String toString(){return "IN";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** negated set membership check. */
|
||||||
public static Op NOT_IN=new Op(){
|
public static Op NOT_IN=new Op(){
|
||||||
public String toString(){return "NOT IN";}
|
public String toString(){return "NOT IN";}
|
||||||
public boolean met(Check c,Object val){
|
public boolean met(Check c,Object val){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** iterator over checks.
|
||||||
|
*
|
||||||
|
*/
|
||||||
public static class CheckIterator implements Iterator<Check>{
|
public static class CheckIterator implements Iterator<Check>{
|
||||||
final Check root;
|
final Check root;
|
||||||
Check cur;
|
Check cur;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -53,15 +60,16 @@ public class Entity extends Hdr{
|
|||||||
* @param cls
|
* @param cls
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public static final Entity publish(Class<? extends DBO> cls){
|
public static final Entity publish(Class<? extends DBO> cls){
|
||||||
Entity ret=registry.get(cls.getSimpleName());
|
Entity ret=registry.get(cls.getSimpleName());
|
||||||
if(ret!=null) return ret;
|
if(ret!=null) return ret;
|
||||||
//System.out.println("Analyzing:"+cls);
|
//System.out.println("Analyzing:"+cls);
|
||||||
Class base=cls.getSuperclass();
|
Class<?> base=cls.getSuperclass();
|
||||||
Entity base_ent=null;
|
Entity base_ent=null;
|
||||||
int position0=0;
|
int position0=0;
|
||||||
if(base!=null && base!=DBO.class){
|
if(base!=null && base!=DBO.class){
|
||||||
base_ent=publish(base);
|
base_ent=publish((Class<? extends DBO>)base);
|
||||||
position0=base_ent.count();
|
position0=base_ent.count();
|
||||||
}
|
}
|
||||||
java.lang.reflect.Field[] declaredFields = cls.getDeclaredFields();
|
java.lang.reflect.Field[] declaredFields = cls.getDeclaredFields();
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -207,8 +214,8 @@ public final class SQL implements Appendable{
|
|||||||
/** fills check values from dbo record where equal and not-equal are used.
|
/** fills check values from dbo record where equal and not-equal are used.
|
||||||
* we place this method here to be as close as possible to the one which generates the sql code.
|
* we place this method here to be as close as possible to the one which generates the sql code.
|
||||||
* check and check_import must be in synch.
|
* check and check_import must be in synch.
|
||||||
* @param filter
|
* @param filter set of checks
|
||||||
* @param params
|
* @param rec record to check
|
||||||
*/
|
*/
|
||||||
public final void check_import(Check filter,DBO rec) {
|
public final void check_import(Check filter,DBO rec) {
|
||||||
if(filter.isLeaf()){
|
if(filter.isLeaf()){
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -11,7 +18,7 @@ import com.reliancy.dbo.Action.Load;
|
|||||||
|
|
||||||
|
|
||||||
/** SQLIterator will delay closing a connection and will iterate over result set.
|
/** SQLIterator will delay closing a connection and will iterate over result set.
|
||||||
*
|
* TODO: no support for orderby yet
|
||||||
*/
|
*/
|
||||||
public class SQLReader implements SiphonIterator<DBO>{
|
public class SQLReader implements SiphonIterator<DBO>{
|
||||||
protected final Entity entity;
|
protected final Entity entity;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
@@ -172,7 +179,14 @@ public class SQLWriter implements Closeable{
|
|||||||
if(rec.getStatus()==DBO.Status.USED){
|
if(rec.getStatus()==DBO.Status.USED){
|
||||||
stmt=updateStmt;
|
stmt=updateStmt;
|
||||||
// update has a pk condition for sure
|
// update has a pk condition for sure
|
||||||
stmt.setObject(supplied.size()+1,pk.get(rec,null),terminal.getTypeId(pk.getType(),pk.getTypeParams()));
|
Object pkval=pk.get(rec,null);
|
||||||
|
if(Handy.isEmpty(pkval)) throw new SQLException("Used object with empty PK");
|
||||||
|
//System.out.println("UPDT:"+stmt+"/"+pkval);
|
||||||
|
stmt.setObject(
|
||||||
|
supplied.size()+1,
|
||||||
|
pkval,
|
||||||
|
terminal.getTypeId(pk.getType(),pk.getTypeParams())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if(stmt==null) return false;
|
if(stmt==null) return false;
|
||||||
// copy values
|
// copy values
|
||||||
@@ -199,7 +213,7 @@ public class SQLWriter implements Closeable{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}else
|
||||||
if(rec.getStatus()==DBO.Status.USED){
|
if(rec.getStatus()==DBO.Status.USED){
|
||||||
this.itemsUpdated+=ucode;
|
this.itemsUpdated+=ucode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -29,7 +36,7 @@ public interface Terminal {
|
|||||||
Entity ent=Entity.recall(cls);
|
Entity ent=Entity.recall(cls);
|
||||||
String sig="/"+ent.getName()+"/load";
|
String sig="/"+ent.getName()+"/load";
|
||||||
try(Action act=begin(sig).load(ent).limit(1).if_pk(id).execute()){
|
try(Action act=begin(sig).load(ent).limit(1).if_pk(id).execute()){
|
||||||
return (T)act.first();
|
return cls.cast(act.first());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public default DBO load(Entity ent,Object...id) throws IOException {
|
public default DBO load(Entity ent,Object...id) throws IOException {
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.reliancy.jabba.sec.SecurityPolicy;
|
||||||
|
import com.reliancy.util.CodeException;
|
||||||
|
import com.reliancy.util.ResultCode;
|
||||||
|
|
||||||
|
/** Base Application class from where specific launchers derive.
|
||||||
|
* Derived classes will usually bring in jetty or tomcat or some other launch ability.
|
||||||
|
*/
|
||||||
|
public abstract class App extends Processor{
|
||||||
|
public static int ERR_NOCONFIG=ResultCode.defineFailure(0x01,App.class,"config missing. provide at least empty one.");
|
||||||
|
public static int ERR_NOTCLOSED=ResultCode.defineFailure(0x02,App.class,"unbalanced call. resource called twice:${resource}");
|
||||||
|
protected Processor first=null;
|
||||||
|
protected Processor last=null;
|
||||||
|
protected RoutedEndPoint router=null;
|
||||||
|
protected SecurityPolicy policy=null;
|
||||||
|
|
||||||
|
public App(String id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
public void before(Request request,Response response) throws IOException{
|
||||||
|
}
|
||||||
|
public void after(Request request,Response response) throws IOException{
|
||||||
|
}
|
||||||
|
public void serve(Request req,Response resp) throws IOException{
|
||||||
|
if(first!=null) first.process(req, resp);
|
||||||
|
if(router!=null) router.process(req,resp);
|
||||||
|
}
|
||||||
|
public <T extends Processor> T addProcessor(T m){
|
||||||
|
if(first==null){
|
||||||
|
last=first=m;
|
||||||
|
}else{
|
||||||
|
last.next=m;
|
||||||
|
}
|
||||||
|
while(last.next!=null) last=last.next;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
public void removeProcessor(Processor m){
|
||||||
|
if(first==m){
|
||||||
|
if(first==last) last=null;
|
||||||
|
first=first.next;
|
||||||
|
while(last!=null && last.next!=null) last=last.next;
|
||||||
|
}else{
|
||||||
|
for(Processor prev=first;prev!=null;prev=prev.next){
|
||||||
|
if(prev.next==m){
|
||||||
|
if(last==m) last=prev;
|
||||||
|
prev.next=m.next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.next=null;
|
||||||
|
}
|
||||||
|
public Processor getProcessor(String id){
|
||||||
|
for(Processor c=first;c!=null;c=c.next){
|
||||||
|
if(c.getId().equalsIgnoreCase(id)) return c;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutedEndPoint getRouter() {
|
||||||
|
return router;
|
||||||
|
}
|
||||||
|
public void setRouter(RoutedEndPoint router) {
|
||||||
|
this.router = router;
|
||||||
|
}
|
||||||
|
public void run(Config conf) throws Exception {
|
||||||
|
try{
|
||||||
|
begin(conf);
|
||||||
|
work();
|
||||||
|
}finally{
|
||||||
|
end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void begin(Config conf) throws Exception{
|
||||||
|
if(config!=null) throw new CodeException(ERR_NOTCLOSED).put("resource","Router.begin()");
|
||||||
|
if(conf==null) throw new CodeException(ERR_NOCONFIG);
|
||||||
|
config=conf;
|
||||||
|
for(Processor p=first;p!=null;p=p.getNext()){
|
||||||
|
p.begin(config);
|
||||||
|
}
|
||||||
|
if(router!=null) router.begin(config);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void end() throws Exception{
|
||||||
|
if(router!=null) router.end();
|
||||||
|
for(Processor p=first;p!=null;p=p.getNext()){
|
||||||
|
p.end();
|
||||||
|
}
|
||||||
|
super.end();
|
||||||
|
log().info("stopping app:"+getId());
|
||||||
|
}
|
||||||
|
public AppSessionFilter addAppSession(){
|
||||||
|
return addProcessor(new AppSessionFilter(this));
|
||||||
|
}
|
||||||
|
public AppSessionFilter addAppSession(AppSession.Factory f){
|
||||||
|
return addProcessor(new AppSessionFilter(this,f));
|
||||||
|
}
|
||||||
|
public SecurityPolicy setSecurityPolicy(SecurityPolicy secpol){
|
||||||
|
if(secpol==policy) return secpol;
|
||||||
|
if(policy!=null){
|
||||||
|
MethodDecorator.retract(policy);
|
||||||
|
removeProcessor(policy);
|
||||||
|
}
|
||||||
|
policy=secpol;
|
||||||
|
if(policy!=null){
|
||||||
|
addProcessor(policy);
|
||||||
|
MethodDecorator.publish(policy); // register security policy as decorator factory
|
||||||
|
}
|
||||||
|
return secpol;
|
||||||
|
}
|
||||||
|
public SecurityPolicy getSecurityPolicy(){
|
||||||
|
return policy;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,33 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import com.reliancy.jabbasec.SecurityActor;
|
import com.reliancy.jabba.sec.SecurityActor;
|
||||||
|
import com.reliancy.jabba.ui.Feedback;
|
||||||
|
|
||||||
public class AppSession implements Session{
|
public class AppSession implements Session{
|
||||||
|
public static interface Factory{
|
||||||
|
AppSession create(String id,App app);
|
||||||
|
}
|
||||||
final String id;
|
final String id;
|
||||||
final HashMap<String,Object> values;
|
final App app;
|
||||||
|
final HashMap<String,Object> values=new HashMap<>();
|
||||||
long timeCreated;
|
long timeCreated;
|
||||||
long lastActive;
|
long lastActive;
|
||||||
long maxAge;
|
long maxAge;
|
||||||
SecurityActor user;
|
SecurityActor user;
|
||||||
|
Feedback feedback;
|
||||||
|
|
||||||
public AppSession(String id){
|
public AppSession(String id,App app){
|
||||||
this.id=id;
|
this.id=id;
|
||||||
values=new HashMap<>();
|
this.app=app;
|
||||||
lastActive=timeCreated=System.currentTimeMillis();
|
lastActive=timeCreated=System.currentTimeMillis();
|
||||||
maxAge=1000*60*15;
|
maxAge=1000*60*15;
|
||||||
}
|
}
|
||||||
@@ -27,6 +40,9 @@ public class AppSession implements Session{
|
|||||||
public Object getValue(String key) {
|
public Object getValue(String key) {
|
||||||
return values.get(key);
|
return values.get(key);
|
||||||
}
|
}
|
||||||
|
public App getApp(){
|
||||||
|
return app;
|
||||||
|
}
|
||||||
public long getTimeInactive(){
|
public long getTimeInactive(){
|
||||||
return System.currentTimeMillis()-lastActive;
|
return System.currentTimeMillis()-lastActive;
|
||||||
}
|
}
|
||||||
@@ -64,4 +80,12 @@ public class AppSession implements Session{
|
|||||||
public void setUser(SecurityActor user){
|
public void setUser(SecurityActor user){
|
||||||
this.user=user;
|
this.user=user;
|
||||||
}
|
}
|
||||||
|
public static AppSession getInstance() {
|
||||||
|
CallSession ss=CallSession.getInstance();
|
||||||
|
return ss!=null?(AppSession)ss.getAppSession():null;
|
||||||
|
}
|
||||||
|
public Feedback getFeedback() {
|
||||||
|
if(feedback==null) feedback=new Feedback();
|
||||||
|
return feedback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -9,8 +16,16 @@ import java.util.UUID;
|
|||||||
*/
|
*/
|
||||||
public class AppSessionFilter extends Processor{
|
public class AppSessionFilter extends Processor{
|
||||||
public static final String KEY_NAME="jbssid";
|
public static final String KEY_NAME="jbssid";
|
||||||
public AppSessionFilter() {
|
AppSession.Factory factory;
|
||||||
super(AppSessionFilter.class.getSimpleName().toLowerCase());
|
App app;
|
||||||
|
public AppSessionFilter(App a) {
|
||||||
|
this(a,null);
|
||||||
|
}
|
||||||
|
public AppSessionFilter(App a,AppSession.Factory f) {
|
||||||
|
super(AppSessionFilter.class.getSimpleName());
|
||||||
|
app=a;
|
||||||
|
if(f==null) f=(id,app)->new AppSession(id, app);
|
||||||
|
factory=f;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void before(Request request, Response response) throws IOException {
|
public void before(Request request, Response response) throws IOException {
|
||||||
@@ -23,7 +38,7 @@ public class AppSessionFilter extends Processor{
|
|||||||
if(ss!=null){
|
if(ss!=null){
|
||||||
if(ss.isExpired()){
|
if(ss.isExpired()){
|
||||||
// this app sessin expired - create a new one
|
// this app sessin expired - create a new one
|
||||||
ss=new AppSession(ssid);
|
ss=factory.create(ssid,app);
|
||||||
AppSession.setInstance(ssid, ss);
|
AppSession.setInstance(ssid, ss);
|
||||||
}else{
|
}else{
|
||||||
// this session is good
|
// this session is good
|
||||||
@@ -31,7 +46,7 @@ public class AppSessionFilter extends Processor{
|
|||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
// no session available
|
// no session available
|
||||||
ss=new AppSession(ssid);
|
ss=factory.create(ssid,app);
|
||||||
AppSession.setInstance(ssid, ss);
|
AppSession.setInstance(ssid, ss);
|
||||||
}
|
}
|
||||||
CallSession css=CallSession.getInstance();
|
CallSession css=CallSession.getInstance();
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -71,11 +78,11 @@ public class CallSession implements Session{
|
|||||||
int len=callers.size();
|
int len=callers.size();
|
||||||
return len>0?callers.get(len-1):null;
|
return len>0?callers.get(len-1):null;
|
||||||
}
|
}
|
||||||
|
public static ThreadLocal<CallSession> instance=new ThreadLocal<>();
|
||||||
/**
|
/**
|
||||||
* Will return current session given the call stack.
|
* Will return current session given the call stack.
|
||||||
* @return
|
* @return thread local call session
|
||||||
*/
|
*/
|
||||||
public static ThreadLocal<CallSession> instance=new ThreadLocal<>();
|
|
||||||
public static CallSession getInstance(){
|
public static CallSession getInstance(){
|
||||||
CallSession ret=instance.get();
|
CallSession ret=instance.get();
|
||||||
if(ret==null) instance.set(ret=new CallSession());
|
if(ret==null) instance.set(ret=new CallSession());
|
||||||
|
|||||||
@@ -1,9 +1,40 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
public interface Config {
|
public interface Config {
|
||||||
|
public static class Property<V> {
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
final Class<V> typ;
|
||||||
|
public Property(String name,Class<V> typ){
|
||||||
|
this.name=name;
|
||||||
|
this.typ=typ;
|
||||||
|
}
|
||||||
|
public String getName(){return name;}
|
||||||
|
public Class<V> getTyp(){return typ;}
|
||||||
|
public V get(Config store,V def){
|
||||||
|
return store.getProperty(this,def);
|
||||||
|
}
|
||||||
|
public V get(Config store){
|
||||||
|
return get(store,null);
|
||||||
|
}
|
||||||
|
public void set(Config store,V val){
|
||||||
|
store.setProperty(this, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static final Property<String> LOG_LEVEL=new Property<String>("LOG_LEVEL",String.class);
|
||||||
|
public static final Property<Logger> LOGGER=new Property<Logger>("LOGGER",Logger.class);
|
||||||
|
|
||||||
public void load();
|
public void load();
|
||||||
public void save();
|
public void save();
|
||||||
public String getId();
|
public String getId();
|
||||||
public Object getProperty(String key,Object def);
|
public <T> Config setProperty(Property<T> key,T val);
|
||||||
public Config setProperty(String key,Object val);
|
public <T> T getProperty(Property<T> key,T def);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class FileConfig implements Config{
|
||||||
|
String path;
|
||||||
|
final HashMap<String,Object> props=new HashMap<>();
|
||||||
|
public FileConfig(String p){
|
||||||
|
path=p;
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
public FileConfig(){
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
public void clear(){
|
||||||
|
props.clear();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void load() {
|
||||||
|
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void save() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public <T> T getProperty(Config.Property<T> key, T def) {
|
||||||
|
if(props.containsKey(key.getName())) return key.getTyp().cast(props.get(key.getName()));
|
||||||
|
else return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Config setProperty(Config.Property<T> key, T val) {
|
||||||
|
props.put(key.getName(),val);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
import com.reliancy.util.Resources;
|
import com.reliancy.util.Resources;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -40,7 +47,7 @@ public class FileServer extends EndPoint implements Resources.PathRewrite{
|
|||||||
@Override
|
@Override
|
||||||
public void serve(Request request, Response response) throws IOException {
|
public void serve(Request request, Response response) throws IOException {
|
||||||
String path=request.getPath();
|
String path=request.getPath();
|
||||||
log().info("serving:"+path);
|
log().debug("to serve:"+path);
|
||||||
for(String prefix:map.keySet()){
|
for(String prefix:map.keySet()){
|
||||||
boolean match=path.startsWith(prefix);
|
boolean match=path.startsWith(prefix);
|
||||||
if(match){
|
if(match){
|
||||||
@@ -49,13 +56,14 @@ public class FileServer extends EndPoint implements Resources.PathRewrite{
|
|||||||
if(!filt.get(prefix).isAcceptable(rpath)) continue; // not acceptable to filter
|
if(!filt.get(prefix).isAcceptable(rpath)) continue; // not acceptable to filter
|
||||||
URL f=Resources.findFirst(this, rpath, sp);
|
URL f=Resources.findFirst(this, rpath, sp);
|
||||||
if(f==null) continue; // skip if rpath not located
|
if(f==null) continue; // skip if rpath not located
|
||||||
System.out.println("RES:"+f);
|
this.log().debug("\tfound:"+f);
|
||||||
writeResource(f,response);
|
writeResource(f,response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response.setStatus(Response.HTTP_NOT_FOUND);
|
response.setStatus(Response.HTTP_NOT_FOUND);
|
||||||
response.getEncoder().writeln("missing file:{0}",path);
|
response.getEncoder().writeln("missing file:{0}",path);
|
||||||
|
this.log().error("not found:"+path);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* we prefix our path for disk and class contexts.
|
* we prefix our path for disk and class contexts.
|
||||||
@@ -111,7 +119,7 @@ public class FileServer extends EndPoint implements Resources.PathRewrite{
|
|||||||
public Iterator<String> enumRoutes(){
|
public Iterator<String> enumRoutes(){
|
||||||
return map.keySet().iterator();
|
return map.keySet().iterator();
|
||||||
}
|
}
|
||||||
public void exportRoutes(RouterEndPoint rep) {
|
public void exportRoutes(RoutedEndPoint rep) {
|
||||||
streamRoutes().forEach(up->rep.addRoute("GET",up+".*",this));
|
streamRoutes().forEach(up->rep.addRoute("GET",up+".*",this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|||||||
@@ -0,0 +1,330 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.EventListener;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.reliancy.jabba.sec.NotAuthentic;
|
||||||
|
import com.reliancy.jabba.sec.Secured;
|
||||||
|
import com.reliancy.jabba.sec.SecurityActor;
|
||||||
|
import com.reliancy.jabba.sec.SecurityPolicy;
|
||||||
|
import com.reliancy.jabba.sec.plain.PlainSecurityStore;
|
||||||
|
import com.reliancy.jabba.ui.Feedback;
|
||||||
|
import com.reliancy.jabba.ui.FeedbackLine;
|
||||||
|
import com.reliancy.jabba.ui.Menu;
|
||||||
|
import com.reliancy.jabba.ui.MenuItem;
|
||||||
|
import com.reliancy.jabba.ui.Rendering;
|
||||||
|
import com.reliancy.jabba.ui.Template;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
import org.eclipse.jetty.server.Handler;
|
||||||
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router is entry point and servlet implementation that dispatches messages to our endpoints.
|
||||||
|
* It will launch an embedded jetty server.
|
||||||
|
* It will provide facilities to register endpoints.
|
||||||
|
*/
|
||||||
|
public class JettyApp extends App implements Handler{
|
||||||
|
enum State{
|
||||||
|
STOPPED,
|
||||||
|
FAILED,
|
||||||
|
STARTING,
|
||||||
|
STARTED,
|
||||||
|
STOPPING,
|
||||||
|
RUNNING
|
||||||
|
}
|
||||||
|
protected Connector[] connectors;
|
||||||
|
protected Server jetty;
|
||||||
|
private volatile State _state;
|
||||||
|
|
||||||
|
public JettyApp() {
|
||||||
|
super("JettyApp");
|
||||||
|
jetty = new Server();
|
||||||
|
jetty.setHandler(this);
|
||||||
|
_state=State.STOPPED;
|
||||||
|
}
|
||||||
|
/** implementation of jetty handler interface */
|
||||||
|
@Override
|
||||||
|
public Server getServer() {
|
||||||
|
return jetty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setServer(Server arg0) {
|
||||||
|
jetty=arg0;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean addEventListener(EventListener arg0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean removeEventListener(EventListener arg0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
protected void setState(State s){
|
||||||
|
_state=s;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isFailed() {
|
||||||
|
return _state==State.FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRunning() {
|
||||||
|
return _state==State.RUNNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStarted() {
|
||||||
|
return _state==State.STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStarting() {
|
||||||
|
return _state==State.STARTING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStopped() {
|
||||||
|
return _state==State.STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStopping() {
|
||||||
|
return _state==State.STOPPING;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void start() throws Exception {
|
||||||
|
_state=State.STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() throws Exception {
|
||||||
|
_state=State.STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Our implementation of a handle process.
|
||||||
|
* In case of exception if we can locate /tempaltes/error.hbs we use it else we re-throw.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void handle(String target,
|
||||||
|
Request baseRequest,
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
com.reliancy.jabba.Request req=new com.reliancy.jabba.Request(request);
|
||||||
|
Response resp=new Response(response);
|
||||||
|
|
||||||
|
CallSession ss=CallSession.getInstance();
|
||||||
|
try{
|
||||||
|
ss.begin(null, req, resp);
|
||||||
|
process(req,resp);
|
||||||
|
}catch(IOException ioex){
|
||||||
|
Template t=Template.find("/templates/error.hbs");
|
||||||
|
if(t==null) throw ioex;
|
||||||
|
Rendering.begin(t)
|
||||||
|
.with(ioex)
|
||||||
|
.end(resp.getEncoder().getWriter());
|
||||||
|
log().error("error:",ioex);
|
||||||
|
}catch(RuntimeException rex){
|
||||||
|
Template t=Template.find("/templates/error.hbs");
|
||||||
|
if(t==null) throw rex;
|
||||||
|
Rendering.begin(t)
|
||||||
|
.with(rex)
|
||||||
|
.end(resp.getEncoder().getWriter());
|
||||||
|
log().error("error:",rex);
|
||||||
|
}finally{
|
||||||
|
ss.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** our own interface specific to jetty engine*/
|
||||||
|
|
||||||
|
public Connector[] getConnectors(){
|
||||||
|
if(connectors!=null) return connectors;
|
||||||
|
ServerConnector connector = new ServerConnector(jetty);
|
||||||
|
connector.setReuseAddress(false);
|
||||||
|
connector.setPort(8090);
|
||||||
|
connectors=new Connector[] {connector};
|
||||||
|
return connectors;
|
||||||
|
}
|
||||||
|
public void begin(Config conf) throws Exception{
|
||||||
|
super.begin(conf);
|
||||||
|
jetty.setConnectors(getConnectors());
|
||||||
|
try{
|
||||||
|
jetty.start();
|
||||||
|
}catch(Exception ex){
|
||||||
|
setState(State.FAILED);
|
||||||
|
if(ex.getCause() instanceof java.net.BindException){
|
||||||
|
log().error("Bind issue",ex);
|
||||||
|
Thread.sleep(3000);
|
||||||
|
}else throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void work() throws InterruptedException{
|
||||||
|
setState(State.RUNNING);
|
||||||
|
if(jetty!=null) jetty.join();
|
||||||
|
}
|
||||||
|
public void end() throws Exception{
|
||||||
|
//setState(State.STOPPING);
|
||||||
|
super.end();
|
||||||
|
Connector[] connectors=jetty.getConnectors();
|
||||||
|
// System.out.println(connectors);
|
||||||
|
if(connectors!=null) for(Connector c:connectors){
|
||||||
|
ServerConnector cc=(ServerConnector) c;
|
||||||
|
//System.out.println("stopping connecor:"+cc);
|
||||||
|
try{
|
||||||
|
cc.stop();
|
||||||
|
cc.getConnectedEndPoints().forEach((endpoint)-> {
|
||||||
|
//System.out.println("closing endpoint:"+endpoint);
|
||||||
|
endpoint.close();
|
||||||
|
});
|
||||||
|
}finally{
|
||||||
|
cc.close();
|
||||||
|
//System.out.println("closing connecor:"+cc.getState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//System.out.println("signaling...");
|
||||||
|
jetty.stop();
|
||||||
|
//setState(State.STOPPED);
|
||||||
|
//System.out.println("cleanup...");
|
||||||
|
System.gc();
|
||||||
|
//System.out.println("return...");
|
||||||
|
}
|
||||||
|
public static void main( String[] args ) throws Exception{
|
||||||
|
//System.out.println("Hello World!");
|
||||||
|
Template.search_path("./var",App.class);
|
||||||
|
JettyApp app=new JettyApp();
|
||||||
|
app.addAppSession();
|
||||||
|
SecurityPolicy secpol=new SecurityPolicy().setStore(new PlainSecurityStore());
|
||||||
|
app.setSecurityPolicy(secpol);
|
||||||
|
RoutedEndPoint rep=new RoutedEndPoint().importMethods(app);
|
||||||
|
app.setRouter(rep);
|
||||||
|
FileServer fs=new FileServer("/static","./var/public");
|
||||||
|
fs.exportRoutes(app.getRouter());
|
||||||
|
Menu top_menu=Menu.request(Menu.TOP);
|
||||||
|
top_menu.add(new MenuItem("home")).addSpacer().add(new MenuItem("login"));
|
||||||
|
top_menu.setTitle("Jabba");
|
||||||
|
app.run(new FileConfig());
|
||||||
|
//System.out.println("Goodbye World!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Routed()
|
||||||
|
public String hello(){
|
||||||
|
Map<String, Object> context = new HashMap<>();
|
||||||
|
context.put("name", "Jared");
|
||||||
|
String ret="";
|
||||||
|
try {
|
||||||
|
Template t=Template.find("/templates/login.hbs");
|
||||||
|
System.out.println("Template:"+t);
|
||||||
|
ret = t.render(context).toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
//#return "Hello World";
|
||||||
|
}
|
||||||
|
@Routed(
|
||||||
|
path="/helloPlain"
|
||||||
|
)
|
||||||
|
public void hello2(com.reliancy.jabba.Request req,Response resp) throws IOException{
|
||||||
|
resp.getEncoder().writeln("Hi There");
|
||||||
|
}
|
||||||
|
@Routed(
|
||||||
|
path="/hello3/{idd:int}"
|
||||||
|
)
|
||||||
|
public String hello3(int id){
|
||||||
|
return "Hello3:"+id;
|
||||||
|
}
|
||||||
|
@Routed(
|
||||||
|
path="/"
|
||||||
|
)
|
||||||
|
public String home(){
|
||||||
|
StringBuilder buf=new StringBuilder();
|
||||||
|
buf.append("<p>Sample pages:</p>");
|
||||||
|
buf.append("<dd><a href='/helloPlain'>plain</a></dd>");
|
||||||
|
buf.append("<dd><a href='/hello3/5'>parametric</a></dd>");
|
||||||
|
buf.append("<dd><a href='/hello'>templated</a></dd>");
|
||||||
|
buf.append("<dd><a href='/secured'>secured http</a></dd>");
|
||||||
|
buf.append("<dd><a href='/secured_form'>secured form</a></dd>");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
@Routed
|
||||||
|
@Secured
|
||||||
|
public String secured(){
|
||||||
|
return "We are secured";
|
||||||
|
}
|
||||||
|
@Routed
|
||||||
|
@Secured(
|
||||||
|
login_form = "/login"
|
||||||
|
)
|
||||||
|
public String secured_form(){
|
||||||
|
return "We are secured by form";
|
||||||
|
}
|
||||||
|
@Routed
|
||||||
|
public void login(com.reliancy.jabba.Request req,Response resp){
|
||||||
|
//return "login form here";
|
||||||
|
if(req.getVerb().equals("POST")){
|
||||||
|
// here we need to process login and redirect
|
||||||
|
try{
|
||||||
|
System.out.println("Post login");
|
||||||
|
String userid=(String)req.getParam("userid",null);
|
||||||
|
String pwd=(String)req.getParam("password",null);
|
||||||
|
AppSession ass=AppSession.getInstance();
|
||||||
|
System.out.println("SS:"+ass);
|
||||||
|
System.out.println("P:"+userid+"/"+pwd);
|
||||||
|
SecurityPolicy secpol=ass.getApp().getSecurityPolicy();
|
||||||
|
SecurityActor user=secpol.authenticate(userid, pwd);
|
||||||
|
if(user==null) throw new NotAuthentic("invalid credentials");
|
||||||
|
resp.setStatus(Response.HTTP_FOUND_REDIRECT);
|
||||||
|
//String old_url=request.getPath();
|
||||||
|
//old_url=URLEncoder.encode(old_url,StandardCharsets.UTF_8.toString());
|
||||||
|
resp.setHeader("Location","/home");
|
||||||
|
}catch(Exception ex){
|
||||||
|
log().error("error:",ex);
|
||||||
|
Feedback.get().push(FeedbackLine.error(ex.getLocalizedMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Map<String, Object> context = new HashMap<>();
|
||||||
|
//context.put("app_title", "Jabba Login");
|
||||||
|
//context.put("name", "Jared");
|
||||||
|
//ArrayList<FeedbackLine> events=new ArrayList<>();
|
||||||
|
|
||||||
|
//Feedback.get().push(FeedbackLine.error("Error"));
|
||||||
|
//Feedback.get().push(FeedbackLine.info("Error"));
|
||||||
|
//Feedback.get().push(FeedbackLine.warn("Error"));
|
||||||
|
//context.put("feedback",events);
|
||||||
|
try {
|
||||||
|
resp.setContentType("text/html");
|
||||||
|
Rendering.begin("/templates/login.hbs")
|
||||||
|
//.with("feedback",events)
|
||||||
|
.end(resp.getEncoder().getWriter());
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/** Decorator is a handler for method annotations.
|
||||||
|
* The name is borrowed from python because it connects java annotations to handler that are called before and after a method is invoked.
|
||||||
|
* Decorator itself is injected and called by methodendpoint as a filter but the factory does not have to return an object.
|
||||||
|
* it can just use the methodendpoint to register it somewhere once during startup.
|
||||||
|
*/
|
||||||
|
public abstract class MethodDecorator {
|
||||||
|
public static interface Factory{
|
||||||
|
MethodDecorator assertDecorator(MethodEndPoint mep,Annotation ann);
|
||||||
|
}
|
||||||
|
static final ArrayList<MethodDecorator.Factory> registry=new ArrayList<>();
|
||||||
|
public static void publish(MethodDecorator.Factory d){
|
||||||
|
if(!registry.contains(d)) registry.add(d);
|
||||||
|
}
|
||||||
|
public static void retract(MethodDecorator.Factory d){
|
||||||
|
while (registry.remove(d));
|
||||||
|
}
|
||||||
|
public static MethodDecorator query(MethodEndPoint mep,Annotation ann){
|
||||||
|
for(MethodDecorator.Factory f:registry){
|
||||||
|
MethodDecorator d=f.assertDecorator(mep, ann);
|
||||||
|
if(d!=null) return d;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
MethodEndPoint method;
|
||||||
|
Annotation annotation;
|
||||||
|
public MethodDecorator(MethodEndPoint mep,Annotation ann){
|
||||||
|
method=mep;
|
||||||
|
annotation=ann;
|
||||||
|
}
|
||||||
|
public abstract void beforeMethod(Request request, Response response);
|
||||||
|
public abstract void afterMethod(Request request, Response response);
|
||||||
|
}
|
||||||
@@ -1,7 +1,16 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Parameter;
|
import java.lang.reflect.Parameter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import com.reliancy.util.Handy;
|
import com.reliancy.util.Handy;
|
||||||
|
|
||||||
@@ -12,16 +21,17 @@ public class MethodEndPoint extends EndPoint{
|
|||||||
FULL, // one or more arguments need to do casting
|
FULL, // one or more arguments need to do casting
|
||||||
|
|
||||||
}
|
}
|
||||||
Route route;
|
Routed route;
|
||||||
Object target;
|
Object target;
|
||||||
Method method;
|
Method method;
|
||||||
Parameter[] params;
|
Parameter[] params;
|
||||||
Class<?> retType;
|
Class<?> retType;
|
||||||
InvokeProfile invokeType;
|
InvokeProfile invokeType;
|
||||||
|
ArrayList<MethodDecorator> decorators=new ArrayList<>();
|
||||||
|
|
||||||
public MethodEndPoint(Object target,Method m,Route r) {
|
public MethodEndPoint(Object target,Method m) {
|
||||||
super(target.getClass().getSimpleName()+"."+m.getName());
|
super(target.getClass().getSimpleName()+"."+m.getName());
|
||||||
this.route=r;
|
this.route=m.getAnnotation(Routed.class);
|
||||||
this.target=target;
|
this.target=target;
|
||||||
this.method=m;
|
this.method=m;
|
||||||
this.params=m.getParameters();
|
this.params=m.getParameters();
|
||||||
@@ -33,8 +43,27 @@ public class MethodEndPoint extends EndPoint{
|
|||||||
if(params.length==0){
|
if(params.length==0){
|
||||||
invokeType=InvokeProfile.NOARG;
|
invokeType=InvokeProfile.NOARG;
|
||||||
}
|
}
|
||||||
|
bindDecorators();
|
||||||
|
}
|
||||||
|
public String getVerb(){
|
||||||
|
return route.verb();
|
||||||
|
}
|
||||||
|
public String getPath() {
|
||||||
|
String ret=route.path();
|
||||||
|
if(!ret.startsWith("/")) ret="/"+ret;
|
||||||
|
ret=ret.replace("{method}",method.getName());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
public Routed getRoute(){
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
/** pulls in and adds decorator filters to this methodcall that are supported. */
|
||||||
|
protected final void bindDecorators(){
|
||||||
|
for(Annotation a:method.getAnnotations()){
|
||||||
|
MethodDecorator d=MethodDecorator.query(this,a);
|
||||||
|
if(d!=null) decorators.add(d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serve(Request request, Response response) throws IOException{
|
public void serve(Request request, Response response) throws IOException{
|
||||||
log().info("Serving method....{}",invokeType);
|
log().info("Serving method....{}",invokeType);
|
||||||
@@ -50,8 +79,7 @@ public class MethodEndPoint extends EndPoint{
|
|||||||
encodeResponse(ret,response);
|
encodeResponse(ret,response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:{
|
default:{ // here we do full unmarshalling, marshalling
|
||||||
// here we do full unmarshalling, marshalling
|
|
||||||
Object[] argVals=decodeRequest(request);
|
Object[] argVals=decodeRequest(request);
|
||||||
ret=method.invoke(target,argVals);
|
ret=method.invoke(target,argVals);
|
||||||
encodeResponse(ret,response);
|
encodeResponse(ret,response);
|
||||||
@@ -62,12 +90,6 @@ public class MethodEndPoint extends EndPoint{
|
|||||||
else throw new IOException(ex2);
|
else throw new IOException(ex2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public String getPath() {
|
|
||||||
String ret=route.path();
|
|
||||||
if(!ret.startsWith("/")) ret="/"+ret;
|
|
||||||
ret=ret.replace("{method}",method.getName());
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
protected Object[] decodeRequest(Request request){
|
protected Object[] decodeRequest(Request request){
|
||||||
Object[] argVals=new Object[params.length];
|
Object[] argVals=new Object[params.length];
|
||||||
for(int i=0;i<argVals.length;i++){
|
for(int i=0;i<argVals.length;i++){
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
/** uri path decoded into tokens. */
|
/** uri path decoded into tokens. */
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -7,7 +14,7 @@ public abstract class Processor {
|
|||||||
protected Processor next;
|
protected Processor next;
|
||||||
protected String id;
|
protected String id;
|
||||||
protected boolean active;
|
protected boolean active;
|
||||||
protected Config config;
|
protected transient Config config;
|
||||||
protected Logger logger;
|
protected Logger logger;
|
||||||
|
|
||||||
public Processor(String id){
|
public Processor(String id){
|
||||||
@@ -63,15 +70,17 @@ public abstract class Processor {
|
|||||||
ss.leave(this);
|
ss.leave(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void begin(Config conf){
|
public void begin(Config conf) throws Exception{
|
||||||
this.config=conf;
|
this.config=conf;
|
||||||
};
|
}
|
||||||
public void end(){
|
public void end() throws Exception{
|
||||||
this.config=null;
|
this.config=null;
|
||||||
};
|
}
|
||||||
|
public void work() throws Exception{
|
||||||
|
}
|
||||||
protected Logger log(){
|
protected Logger log(){
|
||||||
// prefer local over central one
|
// prefer local over central one
|
||||||
Logger ret=logger!=null?logger:(config!=null?(Logger)config.getProperty("logger",null):null);
|
Logger ret=logger!=null?logger:(config!=null?Config.LOGGER.get(config):null);
|
||||||
// if none provided install a fresh one locally
|
// if none provided install a fresh one locally
|
||||||
if(ret==null) ret=logger=LoggerFactory.getLogger(this.getId());
|
if(ret==null) ret=logger=LoggerFactory.getLogger(this.getId());
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -51,7 +58,8 @@ public class Request {
|
|||||||
return http_request.getHeader(key);
|
return http_request.getHeader(key);
|
||||||
}
|
}
|
||||||
public String getCookie(String name,String def){
|
public String getCookie(String name,String def){
|
||||||
for(Cookie c:http_request.getCookies()){
|
Cookie[] all=http_request.getCookies();
|
||||||
|
if(all!=null) for(Cookie c:all){
|
||||||
if(name.equalsIgnoreCase(c.getName())) return c.getValue();
|
if(name.equalsIgnoreCase(c.getName())) return c.getValue();
|
||||||
}
|
}
|
||||||
return def;
|
return def;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -24,6 +31,8 @@ public class Response {
|
|||||||
public static final int HTTP_NOT_FOUND=HttpServletResponse.SC_NOT_FOUND;
|
public static final int HTTP_NOT_FOUND=HttpServletResponse.SC_NOT_FOUND;
|
||||||
public static final int HTTP_UNAUTHORIZED=HttpServletResponse.SC_UNAUTHORIZED;
|
public static final int HTTP_UNAUTHORIZED=HttpServletResponse.SC_UNAUTHORIZED;
|
||||||
public static final int HTTP_FORBIDDEN=HttpServletResponse.SC_FORBIDDEN;
|
public static final int HTTP_FORBIDDEN=HttpServletResponse.SC_FORBIDDEN;
|
||||||
|
public static final int HTTP_TEMPORARY_REDIRECT=HttpServletResponse.SC_TEMPORARY_REDIRECT;
|
||||||
|
public static final int HTTP_FOUND_REDIRECT=HttpServletResponse.SC_FOUND;
|
||||||
|
|
||||||
final protected HttpServletResponse http_response;
|
final protected HttpServletResponse http_response;
|
||||||
final protected Writer char_response;
|
final protected Writer char_response;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -67,7 +74,7 @@ public class ResponseEncoder {
|
|||||||
getOutputStream().write(buf,offset, len);
|
getOutputStream().write(buf,offset, len);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public ResponseEncoder writeString(String str) throws IOException{
|
public ResponseEncoder writeString(CharSequence str) throws IOException{
|
||||||
getWriter().append(str);
|
getWriter().append(str);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -79,11 +86,11 @@ public class ResponseEncoder {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public ResponseEncoder writeln(String msg,Object ... args) throws IOException{
|
public ResponseEncoder writeln(CharSequence msg,Object ... args) throws IOException{
|
||||||
if(args.length==0){
|
if(args.length==0){
|
||||||
getWriter().append(msg).append("\n");
|
getWriter().append(msg).append("\n");
|
||||||
}else{
|
}else{
|
||||||
String str=MessageFormat.format(msg,args);
|
String str=MessageFormat.format(msg.toString(),args);
|
||||||
getWriter().append(str).append("\n");
|
getWriter().append(str).append("\n");
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.reliancy.jabba;
|
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface Route {
|
|
||||||
String path() default "{method}";
|
|
||||||
String verb() default "GET|POST|DELETE";
|
|
||||||
String return_mime() default "";
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import com.reliancy.util.Handy;
|
||||||
|
|
||||||
|
/** Utility class that hosts and matches routes.
|
||||||
|
* This object is needed so we can support parametrized paths.
|
||||||
|
* It will match a route but also extract parameters.
|
||||||
|
*/
|
||||||
|
public class RouteDetector {
|
||||||
|
String verb;
|
||||||
|
String path;
|
||||||
|
String pattern;
|
||||||
|
final ArrayList<String> params=new ArrayList<String>();
|
||||||
|
Pattern regex;
|
||||||
|
Object payload;
|
||||||
|
|
||||||
|
public RouteDetector(String verb,String path){
|
||||||
|
this.verb=verb;
|
||||||
|
this.path=path;
|
||||||
|
if(this.verb==null) this.verb="GET|POST|DELETE";
|
||||||
|
verb=verb.toUpperCase();
|
||||||
|
pattern=toPattern(verb, path);
|
||||||
|
regex=Pattern.compile(pattern);
|
||||||
|
Pattern p=Pattern.compile("\\{(.+)\\}");
|
||||||
|
Matcher m=p.matcher(path);
|
||||||
|
while(m.find()){
|
||||||
|
String g=m.group();
|
||||||
|
params.add(Handy.unwrap(g,"{","}"));
|
||||||
|
}
|
||||||
|
//if(params.isEmpty()==false) routeParams.put(routePat,params);
|
||||||
|
}
|
||||||
|
public String toString(){
|
||||||
|
return getPattern();
|
||||||
|
}
|
||||||
|
public String getPattern(){
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
public String getVerb(){
|
||||||
|
return verb;
|
||||||
|
}
|
||||||
|
public String getPath(){
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
public static String toPattern(String verb,String path){
|
||||||
|
String pathPattern=path.replaceAll("\\{(.+)\\}","(.+)");
|
||||||
|
String ret=Handy.wrap(verb,"(",")")+" "+pathPattern;
|
||||||
|
if(!ret.endsWith("/") && !ret.endsWith("$")) ret+="$";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
public boolean hasParams(){
|
||||||
|
return !params.isEmpty();
|
||||||
|
}
|
||||||
|
public ArrayList<String> getParams(){
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
public boolean matches(String pat){
|
||||||
|
return matches(pat,null);
|
||||||
|
}
|
||||||
|
public boolean matches(String pat,Map<String,String> p){
|
||||||
|
Matcher m=regex.matcher(pat);
|
||||||
|
if(m.find()){ // do we match
|
||||||
|
// we do - now possibly extract params
|
||||||
|
if(p!=null){
|
||||||
|
ArrayList<String> pms=getParams();
|
||||||
|
for(int i=0;i<pms.size();i++){
|
||||||
|
String val=m.group(1+i);
|
||||||
|
String byName=pms.get(i).toLowerCase();
|
||||||
|
p.put(byName,val);
|
||||||
|
p.put("_arg"+i,val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int hashCode(){
|
||||||
|
return getPattern().hashCode();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o){
|
||||||
|
if(o == null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (o == this){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o instanceof RouteDetector && getPattern().equals(((RouteDetector)o).getPattern())){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if( o instanceof String) return matches((String)o,null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public RouteDetector setPayload(Object val){
|
||||||
|
payload=val;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Object getPayload(){
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Routed {
|
||||||
|
String path() default "{method}";
|
||||||
|
String verb() default "GET|POST|DELETE";
|
||||||
|
String return_mime() default "";
|
||||||
|
}
|
||||||
|
|
||||||
+61
-39
@@ -1,24 +1,31 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.reliancy.util.Handy;
|
public class RoutedEndPoint extends EndPoint{
|
||||||
|
HashMap<String,EndPoint> routes=new HashMap<>(); // route pattern to endpoint
|
||||||
public class RouterEndPoint extends EndPoint{
|
ArrayList<RouteDetector> detectors=new ArrayList<>(); // route patterns ordered
|
||||||
HashMap<String,EndPoint> routes=new HashMap<>();
|
|
||||||
HashMap<String,ArrayList<String>> routeParams=new HashMap<>();
|
|
||||||
ArrayList<String> patterns=new ArrayList<>(); // route patterns ordered
|
|
||||||
int[] indexes; // indexes for each route within regex
|
int[] indexes; // indexes for each route within regex
|
||||||
Pattern regex;
|
Pattern regex;
|
||||||
|
|
||||||
public RouterEndPoint() {
|
public RoutedEndPoint() {
|
||||||
super(null);
|
super("router");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -32,7 +39,6 @@ public class RouterEndPoint extends EndPoint{
|
|||||||
if(m!=null){
|
if(m!=null){
|
||||||
//HashMap<String,String> pms=new HashMap<>();
|
//HashMap<String,String> pms=new HashMap<>();
|
||||||
String rt=evalMatcher(m,req.getPathParams());
|
String rt=evalMatcher(m,req.getPathParams());
|
||||||
//System.out.println(rt);
|
|
||||||
//System.out.println(req.getPathParams());
|
//System.out.println(req.getPathParams());
|
||||||
EndPoint ep=getRoute(rt);
|
EndPoint ep=getRoute(rt);
|
||||||
if(ep!=null){
|
if(ep!=null){
|
||||||
@@ -54,40 +60,30 @@ public class RouterEndPoint extends EndPoint{
|
|||||||
return routes.get(r);
|
return routes.get(r);
|
||||||
}
|
}
|
||||||
public void addRoute(String verb,String path, EndPoint mm) {
|
public void addRoute(String verb,String path, EndPoint mm) {
|
||||||
if(verb==null) verb="GET|POST|DELETE";
|
RouteDetector det=new RouteDetector(verb,path);
|
||||||
String pathPat=path.replaceAll("\\{(.+)\\}","(.+)");
|
detectors.add(det);
|
||||||
String routePat=Handy.wrap(verb,"(",")")+" "+pathPat;
|
routes.put(det.getPattern(),mm);
|
||||||
if(!routePat.endsWith("/") && !routePat.endsWith("$")) routePat+="$";
|
|
||||||
routes.put(routePat,mm);
|
|
||||||
//System.out.println("Adding route:"+routePat);
|
|
||||||
ArrayList<String> params=new ArrayList<String>();
|
|
||||||
Pattern p=Pattern.compile("\\{(.+)\\}");
|
|
||||||
Matcher m=p.matcher(path);
|
|
||||||
while(m.find()){
|
|
||||||
String g=m.group();
|
|
||||||
params.add(Handy.unwrap(g,"{","}"));
|
|
||||||
}
|
|
||||||
if(params.isEmpty()==false) routeParams.put(routePat,params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void compile() {
|
public void compile() {
|
||||||
patterns.clear();
|
|
||||||
for(String r:routes.keySet()){
|
|
||||||
patterns.add(r);
|
|
||||||
}
|
|
||||||
// sort with longest first
|
// sort with longest first
|
||||||
Collections.sort(patterns,Comparator.comparing((str)->{return -str.length();}));
|
Collections.sort(detectors,Comparator.comparing((det)->{return -det.getPath().length();}));
|
||||||
String fullPat = "("+String.join(")|(",patterns)+")";
|
String fullPat=detectors
|
||||||
|
.stream()
|
||||||
|
.map(RouteDetector::toString)
|
||||||
|
.collect(Collectors.joining(")|("));
|
||||||
|
fullPat = "("+fullPat+")";
|
||||||
|
//System.out.println("FUll:"+fullPat);
|
||||||
regex=Pattern.compile(fullPat);
|
regex=Pattern.compile(fullPat);
|
||||||
// also recompute indexes
|
// also recompute indexes
|
||||||
indexes=new int[patterns.size()];
|
indexes=new int[detectors.size()];
|
||||||
int index=1;
|
int index=1;
|
||||||
for (int i = 0; i < indexes.length; i++) {
|
for (int i = 0; i < indexes.length; i++) {
|
||||||
indexes[i]=index;
|
indexes[i]=index;
|
||||||
String p=patterns.get(i);
|
RouteDetector det=detectors.get(i);
|
||||||
index+=2; // this includes the verb group
|
index+=2; // this includes the verb group
|
||||||
if(routeParams.containsKey(p)){ // this includes any param groups
|
if(det.hasParams()){ // this includes any param groups
|
||||||
index+=routeParams.get(p).size();
|
index+=det.getParams().size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Arrays.stream(indexes).forEach(e->System.out.println(e+" "));
|
//Arrays.stream(indexes).forEach(e->System.out.println(e+" "));
|
||||||
@@ -102,8 +98,8 @@ public class RouterEndPoint extends EndPoint{
|
|||||||
/**
|
/**
|
||||||
* Find the route and return also url params.
|
* Find the route and return also url params.
|
||||||
* url params are saved in two ways by name and by pos.
|
* url params are saved in two ways by name and by pos.
|
||||||
* @param m
|
* @param m matcher to check
|
||||||
* @param routeParams
|
* @param p parameters to reference
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public String evalMatcher(Matcher m,Map<String,String> p){
|
public String evalMatcher(Matcher m,Map<String,String> p){
|
||||||
@@ -120,9 +116,10 @@ public class RouterEndPoint extends EndPoint{
|
|||||||
if(gindex==indexes[i]) rindex=i;
|
if(gindex==indexes[i]) rindex=i;
|
||||||
}
|
}
|
||||||
if(rindex<0) return null; // we can't match route to group
|
if(rindex<0) return null; // we can't match route to group
|
||||||
String ret=patterns.get(rindex);
|
RouteDetector det=detectors.get(rindex);
|
||||||
if(p!=null && routeParams.containsKey(ret)){
|
//String ret=patterns.get(rindex);
|
||||||
ArrayList<String> pms=routeParams.get(ret);
|
if(p!=null && det.hasParams()){
|
||||||
|
ArrayList<String> pms=det.getParams();
|
||||||
for(int i=0;i<pms.size();i++){
|
for(int i=0;i<pms.size();i++){
|
||||||
String val=m.group(gindex+2+i);
|
String val=m.group(gindex+2+i);
|
||||||
String byName=pms.get(i).toLowerCase();
|
String byName=pms.get(i).toLowerCase();
|
||||||
@@ -130,6 +127,31 @@ public class RouterEndPoint extends EndPoint{
|
|||||||
p.put("_arg"+i,val);
|
p.put("_arg"+i,val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return det.getPattern();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Will import endpoints to serve various paths.
|
||||||
|
* We can call this multiple times for multiple targets.
|
||||||
|
* @param target
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public RoutedEndPoint importMethods(Object target){
|
||||||
|
//RoutedEndPoint ret=new RoutedEndPoint();
|
||||||
|
LinkedList<Method> routes=new LinkedList<>();
|
||||||
|
Class<?> type=target.getClass();
|
||||||
|
while (type != null) {
|
||||||
|
for(Method m : type.getDeclaredMethods()){
|
||||||
|
//System.out.println("Method:"+m.toString());
|
||||||
|
if(m.getAnnotation(Routed.class)!=null){
|
||||||
|
routes.add(0,m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type = type.getSuperclass();
|
||||||
|
}
|
||||||
|
for(Method m:routes){
|
||||||
|
MethodEndPoint mm=new MethodEndPoint(target,m);
|
||||||
|
addRoute(mm.getVerb(),mm.getPath(),mm);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,231 +0,0 @@
|
|||||||
package com.reliancy.jabba;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.reliancy.jabbasec.SecurityPolicy;
|
|
||||||
import com.reliancy.util.Template;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.server.Connector;
|
|
||||||
import org.eclipse.jetty.server.Request;
|
|
||||||
import org.eclipse.jetty.server.Server;
|
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
|
||||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Router is entry point and servlet implementation that dispatches messages to our endpoints.
|
|
||||||
* It will launch an embedded jetty server.
|
|
||||||
* It will provide facilities to register endpoints.
|
|
||||||
*/
|
|
||||||
public class Router extends AbstractHandler{
|
|
||||||
protected Connector[] connectors;
|
|
||||||
protected Server jetty;
|
|
||||||
protected Processor first=null;
|
|
||||||
protected Processor last=null;
|
|
||||||
protected RouterEndPoint main=null;
|
|
||||||
protected transient Config config=null;
|
|
||||||
protected Logger logger=LoggerFactory.getLogger(Router.class);
|
|
||||||
|
|
||||||
public Router() {
|
|
||||||
jetty = new Server();
|
|
||||||
jetty.setHandler(this);
|
|
||||||
}
|
|
||||||
public Config getConfig(){
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
public void addProcessor(Processor m){
|
|
||||||
if(first==null){
|
|
||||||
last=first=m;
|
|
||||||
}else{
|
|
||||||
last.next=m;
|
|
||||||
}
|
|
||||||
while(last.next!=null) last=last.next;
|
|
||||||
}
|
|
||||||
public void removeProcessor(Processor m){
|
|
||||||
if(first==m){
|
|
||||||
if(first==last) last=null;
|
|
||||||
first=first.next;
|
|
||||||
while(last!=null && last.next!=null) last=last.next;
|
|
||||||
}else{
|
|
||||||
for(Processor prev=first;prev!=null;prev=prev.next){
|
|
||||||
if(prev.next==m){
|
|
||||||
if(last==m) last=prev;
|
|
||||||
prev.next=m.next;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m.next=null;
|
|
||||||
}
|
|
||||||
public Processor getProcessor(String id){
|
|
||||||
for(Processor c=first;c!=null;c=c.next){
|
|
||||||
if(c.getId().equalsIgnoreCase(id)) return c;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RouterEndPoint getMain() {
|
|
||||||
return main;
|
|
||||||
}
|
|
||||||
public void setMain(RouterEndPoint resolver) {
|
|
||||||
this.main = resolver;
|
|
||||||
}
|
|
||||||
public RouterEndPoint importEndPoints(Object target){
|
|
||||||
RouterEndPoint ret=new RouterEndPoint();
|
|
||||||
LinkedList<Method> routes=new LinkedList<>();
|
|
||||||
Class<?> type=target.getClass();
|
|
||||||
while (type != null) {
|
|
||||||
for(Method m : type.getDeclaredMethods()){
|
|
||||||
//System.out.println("Method:"+m.toString());
|
|
||||||
if(m.getAnnotation(Route.class)!=null){
|
|
||||||
routes.add(0,m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type = type.getSuperclass();
|
|
||||||
}
|
|
||||||
for(Method m:routes){
|
|
||||||
//System.out.println("M:"+m);
|
|
||||||
Route r=m.getAnnotation(Route.class);
|
|
||||||
MethodEndPoint mm=new MethodEndPoint(target,m,r);
|
|
||||||
ret.addRoute(r.verb(),mm.getPath(),mm);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handle(String target,
|
|
||||||
Request baseRequest,
|
|
||||||
HttpServletRequest request,
|
|
||||||
HttpServletResponse response)
|
|
||||||
throws IOException, ServletException
|
|
||||||
{
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
com.reliancy.jabba.Request req=new com.reliancy.jabba.Request(request);
|
|
||||||
Response resp=new Response(response);
|
|
||||||
CallSession ss=CallSession.getInstance();
|
|
||||||
try{
|
|
||||||
ss.begin(null, req, resp);
|
|
||||||
if(first!=null) first.process(req, resp);
|
|
||||||
if(main!=null) main.process(req,resp);
|
|
||||||
}finally{
|
|
||||||
ss.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Connector[] getConnectors(){
|
|
||||||
if(connectors!=null) return connectors;
|
|
||||||
ServerConnector connector = new ServerConnector(jetty);
|
|
||||||
connector.setReuseAddress(false);
|
|
||||||
connector.setPort(8090);
|
|
||||||
connectors=new Connector[] {connector};
|
|
||||||
return connectors;
|
|
||||||
}
|
|
||||||
public void begin(Config conf) throws Exception{
|
|
||||||
if(config!=null) throw new RuntimeException("Router running already");
|
|
||||||
config=conf;
|
|
||||||
for(Processor p=first;p!=null;p=p.getNext()){
|
|
||||||
p.begin(config);
|
|
||||||
}
|
|
||||||
if(main!=null) main.begin(config);
|
|
||||||
jetty.setConnectors(getConnectors());
|
|
||||||
try{
|
|
||||||
jetty.start();
|
|
||||||
}catch(Exception ex){
|
|
||||||
if(ex.getCause() instanceof java.net.BindException){
|
|
||||||
logger.error("Bind issue",ex);
|
|
||||||
Thread.sleep(3000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void end() throws Exception{
|
|
||||||
if(main!=null) main.end();
|
|
||||||
for(Processor p=first;p!=null;p=p.getNext()){
|
|
||||||
p.end();
|
|
||||||
}
|
|
||||||
config=null;
|
|
||||||
logger.info("stopiing jetty");
|
|
||||||
Connector[] connectors=jetty.getConnectors();
|
|
||||||
System.out.println(connectors);
|
|
||||||
if(connectors!=null) for(Connector c:connectors){
|
|
||||||
ServerConnector cc=(ServerConnector) c;
|
|
||||||
//System.out.println("stopping connecor:"+cc);
|
|
||||||
try{
|
|
||||||
cc.stop();
|
|
||||||
cc.getConnectedEndPoints().forEach((endpoint)-> {
|
|
||||||
//System.out.println("closing endpoint:"+endpoint);
|
|
||||||
endpoint.close();
|
|
||||||
});
|
|
||||||
}finally{
|
|
||||||
cc.close();
|
|
||||||
//System.out.println("closing connecor:"+cc.getState());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//System.out.println("signaling...");
|
|
||||||
jetty.stop();
|
|
||||||
//System.out.println("cleanup...");
|
|
||||||
System.gc();
|
|
||||||
//System.out.println("return...");
|
|
||||||
}
|
|
||||||
public void run(Config conf) throws Exception {
|
|
||||||
try{
|
|
||||||
begin(conf);
|
|
||||||
//System.out.println("Entering server loop...");
|
|
||||||
jetty.join();
|
|
||||||
}finally{
|
|
||||||
//System.out.println("Exiting server loop...");
|
|
||||||
end();
|
|
||||||
//System.out.println("Exiting server loop...done");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static void main( String[] args ) throws Exception
|
|
||||||
{
|
|
||||||
//System.out.println("Hello World!");
|
|
||||||
Router app=new Router();
|
|
||||||
app.addProcessor(new AppSessionFilter());
|
|
||||||
app.addProcessor(new SecurityPolicy());
|
|
||||||
app.setMain(app.importEndPoints(app));
|
|
||||||
FileServer fs=new FileServer("/static","./var");
|
|
||||||
fs.exportRoutes(app.getMain());
|
|
||||||
app.run(null);
|
|
||||||
//System.out.println("Goodbye World!");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Route()
|
|
||||||
public String hello(){
|
|
||||||
Map<String, Object> context = new HashMap<>();
|
|
||||||
context.put("name", "Jared");
|
|
||||||
String ret="";
|
|
||||||
try {
|
|
||||||
Template.search_path("./var",SecurityPolicy.class);
|
|
||||||
Template t=Template.find("/templates/login.hbs");
|
|
||||||
System.out.println("Template:"+t);
|
|
||||||
ret = t.render(context).toString();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
//#return "Hello World";
|
|
||||||
}
|
|
||||||
@Route(
|
|
||||||
path="/helloPlain"
|
|
||||||
)
|
|
||||||
public void hello2(com.reliancy.jabba.Request req,Response resp) throws IOException{
|
|
||||||
resp.getEncoder().writeln("Hi There");
|
|
||||||
}
|
|
||||||
@Route(
|
|
||||||
path="/hello3/{idd:int}"
|
|
||||||
)
|
|
||||||
public String hello3(int id){
|
|
||||||
return "Hello3:"+id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
/** Session is temporary storage.
|
/** Session is temporary storage.
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec;
|
||||||
|
|
||||||
|
import com.reliancy.util.CodeException;
|
||||||
|
import com.reliancy.util.ResultCode;
|
||||||
|
|
||||||
|
public class NeedCredentials extends CodeException {
|
||||||
|
public static final int CODE=ResultCode.defineFailure(1,SecurityPolicy.class,"please provide credentials");
|
||||||
|
|
||||||
|
public NeedCredentials(){
|
||||||
|
super(CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec;
|
||||||
|
|
||||||
|
public class NotAuthentic extends RuntimeException {
|
||||||
|
public NotAuthentic(String message){
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
public NotAuthentic(String message,Throwable cause){
|
||||||
|
super(message,cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec;
|
||||||
|
/**
|
||||||
|
* Our own exception to throw when we are not allowed access.
|
||||||
|
*/
|
||||||
|
public class NotPermitted extends RuntimeException{
|
||||||
|
public NotPermitted(String message){
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
public NotPermitted(String message,Throwable cause){
|
||||||
|
super(message,cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.reliancy.util.Handy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any entity that can be secured or permissions set for an actor.
|
||||||
|
*/
|
||||||
|
public interface Securable {
|
||||||
|
public SecurityStore getStore();
|
||||||
|
public Integer getId();
|
||||||
|
default public Integer getOwnerId(){
|
||||||
|
return getOwner().getId();
|
||||||
|
}
|
||||||
|
public String getKind();
|
||||||
|
public String getName();
|
||||||
|
default public String getTitle(){
|
||||||
|
return Handy.prettyPrint(getName());
|
||||||
|
}
|
||||||
|
public String getIcon();
|
||||||
|
public Securable getOwner();
|
||||||
|
public List<Securable> getOwnedSecurables();
|
||||||
|
public List<SecurityPermit> getDirectPermits();
|
||||||
|
public SecurityPermit getPermit(Securable sec);
|
||||||
|
public SecurityPolicy getPolicy();
|
||||||
|
public default boolean isEssential(){
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
/** Annotation to indicate that this resource requruies authenticated actor and/or permit.
|
||||||
|
* first of all we register this route with securitypolicy and enforce actor presence.
|
||||||
|
* if user is not logged in we send to login form or use one of our protocols.
|
||||||
|
* Additionally we can specify permits that are required.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Secured {
|
||||||
|
String login_form() default "";
|
||||||
|
String permits() default "";
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that is implemented by any User or Principal entity.
|
||||||
|
* Often an AppSession will be determined in many ways by the user.
|
||||||
|
* SecurityActor is a very special type of Securable.
|
||||||
|
*/
|
||||||
|
public interface SecurityActor extends Securable{
|
||||||
|
SecurityActor authPassword(String password);
|
||||||
|
/** returns HA1 signature for Digest protocol. */
|
||||||
|
String getDigestSignature(String realm);
|
||||||
|
default boolean isRole(){
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
+11
-1
@@ -1,4 +1,11 @@
|
|||||||
package com.reliancy.jabbasec;
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec;
|
||||||
/**
|
/**
|
||||||
* An object describing what rights an actor has on a securable.
|
* An object describing what rights an actor has on a securable.
|
||||||
* This object can be one individual rule or an effective merge of multiple rights.
|
* This object can be one individual rule or an effective merge of multiple rights.
|
||||||
@@ -6,6 +13,8 @@ package com.reliancy.jabbasec;
|
|||||||
* We should start implementing from security policy and maybe we do not even need this class.
|
* We should start implementing from security policy and maybe we do not even need this class.
|
||||||
*/
|
*/
|
||||||
public interface SecurityPermit {
|
public interface SecurityPermit {
|
||||||
|
public SecurityStore getStore();
|
||||||
|
public Integer getId();
|
||||||
public SecurityActor getActor();
|
public SecurityActor getActor();
|
||||||
public Securable getSubject();
|
public Securable getSubject();
|
||||||
public boolean canRead();
|
public boolean canRead();
|
||||||
@@ -13,4 +22,5 @@ public interface SecurityPermit {
|
|||||||
public boolean canDelete();
|
public boolean canDelete();
|
||||||
public boolean canCreate();
|
public boolean canCreate();
|
||||||
public boolean canSecure();
|
public boolean canSecure();
|
||||||
|
public boolean canExecute();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import com.reliancy.jabba.AppSession;
|
||||||
|
import com.reliancy.jabba.CallSession;
|
||||||
|
import com.reliancy.jabba.Processor;
|
||||||
|
import com.reliancy.jabba.Request;
|
||||||
|
import com.reliancy.jabba.Response;
|
||||||
|
import com.reliancy.jabba.RouteDetector;
|
||||||
|
import com.reliancy.jabba.MethodDecorator;
|
||||||
|
import com.reliancy.jabba.MethodEndPoint;
|
||||||
|
import com.reliancy.util.CodeException;
|
||||||
|
import com.reliancy.util.Handy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SecurityPolicy is a filter/processor that implements various auth protocols but also sources users.
|
||||||
|
* The policy will produce new users once they are authenticated.
|
||||||
|
* SecurityPolicy will authenticate SecurityActors and possibly authorize them via a permission mechanism
|
||||||
|
* to access.
|
||||||
|
* SecurityProtocol is one authenticatio method. We will not adjust response only try to recover user.
|
||||||
|
* Initialization of auth will occur outside:
|
||||||
|
* - for gui after login we will set an auth cookie to remember user and password
|
||||||
|
* - for APIs if user is required will issue error 401 with WWW-Authenticate header
|
||||||
|
*/
|
||||||
|
public class SecurityPolicy extends Processor implements MethodDecorator.Factory{
|
||||||
|
public static String REALM="reliancy";
|
||||||
|
public static final String KEY_NAME="jbauth";
|
||||||
|
protected String secret="sdfklgj 7150 9178-54=09";
|
||||||
|
protected ArrayList<SecurityProtocol> protocols;
|
||||||
|
protected SecurityActor admin;
|
||||||
|
protected SecurityActor guest;
|
||||||
|
protected final HashMap<Pattern,Secured> secured_pat=new HashMap<>(); // paths that require user
|
||||||
|
protected SecurityStore store;
|
||||||
|
|
||||||
|
public SecurityPolicy() {
|
||||||
|
super(SecurityPolicy.class.getSimpleName().toLowerCase());
|
||||||
|
protocols=new ArrayList<>();
|
||||||
|
protocols.add(new SecurityProtocol.Digest());
|
||||||
|
protocols.add(new SecurityProtocol.Basic());
|
||||||
|
}
|
||||||
|
protected String getSecret(){
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
public SecurityPolicy setSecured(String path,Secured info){
|
||||||
|
if(checkSecured(path)!=null) throw new IllegalStateException("Secured path cannot be secured again:"+path);
|
||||||
|
Pattern regex=Pattern.compile(path);
|
||||||
|
secured_pat.put(regex,info);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Secured checkSecured(String path){
|
||||||
|
for(Pattern p:secured_pat.keySet()){
|
||||||
|
if(p.pattern().equals(path)) return secured_pat.get(p);
|
||||||
|
if(p.matcher(path).find()) return secured_pat.get(p);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void before(Request request, Response response) throws IOException {
|
||||||
|
// we will recover a user here
|
||||||
|
CallSession css=CallSession.getInstance();
|
||||||
|
AppSession ass=(AppSession) css.getAppSession();
|
||||||
|
if(ass==null || ass.getUser()!=null){
|
||||||
|
return; // we got a user all good
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
SecurityActor user=authenticate(request);
|
||||||
|
if(user!=null) ass.setUser(user);
|
||||||
|
}catch(NotAuthentic bad_cred){
|
||||||
|
// we could not establish user
|
||||||
|
response.setStatus(Response.HTTP_FORBIDDEN);
|
||||||
|
response.getEncoder().writeObject(CodeException.getUserMessage(bad_cred));
|
||||||
|
}catch(NeedCredentials no_cred){
|
||||||
|
String login_form=no_cred.get("login_form");
|
||||||
|
if(Handy.isBlank(login_form)){
|
||||||
|
// we got no login form use HTTP auth
|
||||||
|
response.setStatus(Response.HTTP_UNAUTHORIZED);
|
||||||
|
String auth_supported=protocols.get(0).getSignature(REALM);
|
||||||
|
//String auth_supported=protocols.stream().map(SecurityProtocol::getName).collect(Collectors.joining(","));
|
||||||
|
response.setHeader("WWW-Authenticate",auth_supported);
|
||||||
|
}else{
|
||||||
|
// we got a login form do a redirect
|
||||||
|
response.setStatus(Response.HTTP_FOUND_REDIRECT);
|
||||||
|
String old_url=request.getPath();
|
||||||
|
old_url=URLEncoder.encode(old_url,StandardCharsets.UTF_8.toString());
|
||||||
|
//old_url=flask.escape(request.url.replace(request.url_root.strip("/"),""))
|
||||||
|
//resp=flask.redirect("{}?next={}".format(login_pg,old_url),code=303)
|
||||||
|
response.setHeader("Location",login_form+"?next="+old_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void after(Request request, Response response) throws IOException {
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void serve(Request request, Response response) throws IOException {
|
||||||
|
// nothing to do here
|
||||||
|
}
|
||||||
|
/** authenticates or establishes user based on user and password.
|
||||||
|
* same as loadActor but with first param being admin account.
|
||||||
|
* @param name userid
|
||||||
|
* @param pwd password
|
||||||
|
* @return user we could establish
|
||||||
|
* @throws NotPermitted
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public SecurityActor authenticate(String name, String pwd) throws NotPermitted, IOException{
|
||||||
|
return loadActor(admin, name, pwd);
|
||||||
|
}
|
||||||
|
/** authenticates or establishes user based on request and updates response.
|
||||||
|
* this method might redirect to a login view. once it is done
|
||||||
|
* @param req
|
||||||
|
* @return user we could establish
|
||||||
|
* @throws NotPermitted
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public SecurityActor authenticate(Request req) throws IOException, NotAuthentic, NeedCredentials{
|
||||||
|
// must recover user from cookies or by redirecting to login
|
||||||
|
String verb=req.getVerb();
|
||||||
|
String path=req.getPath();
|
||||||
|
log().info("Path:"+path);
|
||||||
|
String secpath=verb+" "+path;
|
||||||
|
Secured secinfo=checkSecured(secpath);
|
||||||
|
if(secinfo==null){
|
||||||
|
return null; // this path is not secured
|
||||||
|
}
|
||||||
|
log().info("\tuser is needed, send back auth resp");
|
||||||
|
String auth=req.getCookie(KEY_NAME,null);
|
||||||
|
//log().info("Auth1:"+auth);
|
||||||
|
if(auth!=null){
|
||||||
|
// we have an auth cookie - encoded user login
|
||||||
|
Map<String,String> kv=Handy.decrypt(getSecret(),auth);
|
||||||
|
String username=(kv.get("n"));
|
||||||
|
String password=(kv.get("p"));
|
||||||
|
String address=(kv.get("a"));
|
||||||
|
if(address!=null && !address.equals(req.getRemoteAddress())){
|
||||||
|
return null; // invalid auth cookie
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SecurityActor user = loadActor(admin,username,password);
|
||||||
|
if(user!=null) return user;
|
||||||
|
else throw new NotAuthentic("invalid credentials");
|
||||||
|
} catch (NotPermitted e) {
|
||||||
|
throw new NotAuthentic("not permitted to authenticate",e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// try authorization header as fallback - can't clear it always
|
||||||
|
auth=req.getHeader("Authorization");
|
||||||
|
//log().info("Auth2:"+auth);
|
||||||
|
if(auth!=null){
|
||||||
|
String[] kv=auth.split(" ",2);
|
||||||
|
String proto_name=kv[0];
|
||||||
|
String proto_args=kv.length>1?kv[1]:"";
|
||||||
|
for(SecurityProtocol sproto:protocols){
|
||||||
|
if(proto_name.equalsIgnoreCase(sproto.getName())){
|
||||||
|
return sproto.authenticate(this, req,proto_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NotAuthentic("auth method not supported:"+proto_name);
|
||||||
|
}
|
||||||
|
throw new NeedCredentials().put("login_form",secinfo.login_form());
|
||||||
|
//return null;
|
||||||
|
}
|
||||||
|
/** will establish what if any rights an actor has on a securable. */
|
||||||
|
public SecurityPermit authorize(SecurityActor actor, Securable subject){
|
||||||
|
return actor.getPermit(subject);
|
||||||
|
}
|
||||||
|
public SecurityPolicy setStore(SecurityStore store) throws NotPermitted, IOException{
|
||||||
|
// this call will work unless store is locked already
|
||||||
|
guest=(SecurityActor)store.loadSecurable(null,SecurityStore.GUEST);
|
||||||
|
admin=(SecurityActor)store.loadSecurable(null,SecurityStore.ADMIN);
|
||||||
|
if(guest==null) throw new IllegalArgumentException("store is missing guest actor");
|
||||||
|
if(admin==null) throw new IllegalArgumentException("store is missing admin actor");
|
||||||
|
// now we lock store
|
||||||
|
store.setPolicy(this);
|
||||||
|
this.store=store;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public SecurityStore getStore(){
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
/** will save a securable including a user if permitted via actor. */
|
||||||
|
public void saveSecurable(SecurityActor actor, Securable sec) throws IOException{
|
||||||
|
store.saveSecurable(actor, sec);
|
||||||
|
}
|
||||||
|
/** loads a securable by id given actor permits. */
|
||||||
|
public Securable loadSecurable(SecurityActor actor, Integer id) throws IOException, NotPermitted{
|
||||||
|
return store.loadSecurable(actor, id);
|
||||||
|
}
|
||||||
|
/** loads an actor given name and/or password.
|
||||||
|
* if actor is an admin and no password is given it looks up actor by name.
|
||||||
|
*/
|
||||||
|
public SecurityActor loadActor(SecurityActor actor, String name, String pwd) throws IOException, NotPermitted{
|
||||||
|
return store.loadActor(actor, name, pwd);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* we do not create actual decorators we just register this method so we can intercept routes.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public MethodDecorator assertDecorator(MethodEndPoint mep, Annotation ann) {
|
||||||
|
if(!(ann instanceof Secured)) return null;
|
||||||
|
System.out.println("Assert decorator for:"+mep.getPath());
|
||||||
|
String verb=mep.getVerb();
|
||||||
|
String path=mep.getPath();
|
||||||
|
String pat=RouteDetector.toPattern(verb, path);
|
||||||
|
setSecured(pat,(Secured)ann);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.reliancy.jabba.Request;
|
||||||
|
import com.reliancy.util.Handy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A SecurityProtocol will be processing HTTP to establish SecurityActor or user.
|
||||||
|
*/
|
||||||
|
public abstract class SecurityProtocol {
|
||||||
|
protected final String name;
|
||||||
|
public SecurityProtocol(String n){
|
||||||
|
name=n;
|
||||||
|
}
|
||||||
|
public String getName(){
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
/// returns the signature to be send to client.
|
||||||
|
public String getSignature(String realm){
|
||||||
|
return getName()+String.format(" realm=\"%s\"",realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) throws NotPermitted, IOException ;
|
||||||
|
public static class Basic extends SecurityProtocol{
|
||||||
|
public Basic(){
|
||||||
|
super("Basic");
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) throws NotPermitted, IOException {
|
||||||
|
tok=String.valueOf(Handy.decodeBase64(tok)) ;
|
||||||
|
String[] up=tok.split(":",2);
|
||||||
|
String userid=up[0];
|
||||||
|
String pwd=up.length>1?up[1]:"";
|
||||||
|
return policy.loadActor(policy.admin,userid,pwd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static class Digest extends SecurityProtocol{
|
||||||
|
public Digest(){
|
||||||
|
super("Digest");
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) throws NotPermitted, IOException{
|
||||||
|
Map<String,String> params=decode_params(tok);
|
||||||
|
String username=params.get("username");
|
||||||
|
String realm=params.get("realm");
|
||||||
|
String nonce=params.get("nonce");
|
||||||
|
//String uri=params.get("uri");
|
||||||
|
String rsp_in=params.get("response");
|
||||||
|
SecurityActor usr=policy.loadActor(policy.admin,username,null);
|
||||||
|
//#print("USER:",usr)
|
||||||
|
if(usr!=null){
|
||||||
|
String ha1=usr.getDigestSignature(realm);
|
||||||
|
String rsp_usr=digest_signature(ha1,nonce,req.getVerb(),req.getPath());
|
||||||
|
//print("CHECK:",rsp_usr,rsp_in,uri,req.full_path)
|
||||||
|
if(!rsp_usr.equals(rsp_in)){
|
||||||
|
//this user response does not match one provided
|
||||||
|
usr=null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(usr==null) throw new NotAuthentic("Invalid credentials");
|
||||||
|
return usr;
|
||||||
|
}
|
||||||
|
public String digest_signature(String ha1,String nonce,String method,String uri){
|
||||||
|
//ha1_msg=user+":"+realm+":"+password
|
||||||
|
//ha1=self.get_md5(ha1_msg)
|
||||||
|
String ha2_msg=method+":"+uri;
|
||||||
|
String ha2=Handy.hashMD5(ha2_msg);
|
||||||
|
String rsp_msg=ha1+":"+nonce+":"+ha2;
|
||||||
|
String rsp=Handy.hashMD5(rsp_msg);
|
||||||
|
return rsp;
|
||||||
|
}
|
||||||
|
/** return a dict of all the values sent.*/
|
||||||
|
public Map<String,String> decode_params(String tok){
|
||||||
|
HashMap<String,String> ret=new HashMap<>();
|
||||||
|
for(String kv:tok.trim().split(",")){
|
||||||
|
String[] args=kv.trim().split("=",2);
|
||||||
|
String k=Handy.trimRight(args[0]," \t\r\f\n");
|
||||||
|
String v=args.length>1?Handy.trimEvenly(args[1],"'\""):"";
|
||||||
|
ret.put(k,v);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/// returns a new nonce value.
|
||||||
|
public String new_nonce(){
|
||||||
|
//String nonce=util.get_md5(str(id(self)))
|
||||||
|
String nonce=Handy.hashMD5(String.valueOf(hashCode()));
|
||||||
|
return nonce;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getSignature(String realm){
|
||||||
|
//return getName()+String.format(" realm=\"%s\"",realm);
|
||||||
|
String nonce=new_nonce();
|
||||||
|
String ret=super.getSignature(realm);
|
||||||
|
return String.format("%s, nonce=\"%s\"",ret,nonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage interface for security elemenets such as securables, actors and permits.
|
||||||
|
* Storage is unlocked until a policy is set then it gets locked and it matters who
|
||||||
|
* admin actor is.
|
||||||
|
*
|
||||||
|
* The lock is establish witht the install of a policy. The outside code including policy can
|
||||||
|
* query any securable and especially admin account. Once the policy is set then admin account
|
||||||
|
* must be used for privileged access.
|
||||||
|
*/
|
||||||
|
public interface SecurityStore {
|
||||||
|
public static int GUEST=-1;
|
||||||
|
public static int ADMIN=-2;
|
||||||
|
|
||||||
|
public Securable newSecurable();
|
||||||
|
public SecurityActor newActor();
|
||||||
|
public SecurityPermit newPermit();
|
||||||
|
public void deleteSecurable(SecurityActor actor, Securable sec) throws IOException;
|
||||||
|
public void saveSecurable(SecurityActor actor, Securable sec) throws IOException;
|
||||||
|
public Securable loadSecurable(SecurityActor actor, Integer id) throws IOException, NotPermitted;
|
||||||
|
public SecurityActor loadActor(SecurityActor actor, String name, String pwd) throws IOException, NotPermitted;
|
||||||
|
public List<Securable> loadSecurables(SecurityActor actor, Securable sec) throws IOException, NotPermitted;
|
||||||
|
public void deletePermit(SecurityActor actor, SecurityPermit permit) throws IOException;
|
||||||
|
public void savePermit(SecurityActor actor, SecurityPermit permit) throws IOException;
|
||||||
|
public SecurityPermit loadPermit(SecurityActor actor, Integer id) throws IOException, NotPermitted;
|
||||||
|
public List<SecurityPermit> loadPermitsBy(SecurityActor actor, SecurityActor sec) throws IOException, NotPermitted;
|
||||||
|
public List<SecurityPermit> loadPermitsOn(SecurityActor actor, Securable sec) throws IOException, NotPermitted;
|
||||||
|
public void setPolicy(SecurityPolicy policy);
|
||||||
|
public SecurityPolicy getPolicy();
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec.plain;
|
||||||
|
|
||||||
|
import com.reliancy.jabba.sec.SecurityActor;
|
||||||
|
import com.reliancy.util.Handy;
|
||||||
|
|
||||||
|
public class PlainActor extends PlainSecurable implements SecurityActor{
|
||||||
|
String password;
|
||||||
|
boolean role;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityActor authPassword(String pwd) {
|
||||||
|
if(password.equals(pwd)) return this;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PlainActor setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDigestSignature(String realm){
|
||||||
|
String ha1_msg=this.getName()+":"+realm+":"+password;
|
||||||
|
String ha1=Handy.hashMD5(ha1_msg);
|
||||||
|
return ha1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRole(boolean role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec.plain;
|
||||||
|
|
||||||
|
import com.reliancy.jabba.sec.Securable;
|
||||||
|
import com.reliancy.jabba.sec.SecurityActor;
|
||||||
|
import com.reliancy.jabba.sec.SecurityPermit;
|
||||||
|
import com.reliancy.jabba.sec.SecurityStore;
|
||||||
|
|
||||||
|
public class PlainPermit implements SecurityPermit{
|
||||||
|
SecurityStore store;
|
||||||
|
Integer id;
|
||||||
|
SecurityActor actor;
|
||||||
|
Securable subject;
|
||||||
|
boolean can_read;
|
||||||
|
boolean can_write;
|
||||||
|
boolean can_delete;
|
||||||
|
boolean can_create;
|
||||||
|
boolean can_secure;
|
||||||
|
boolean can_execute;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityStore getStore(){
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityActor getActor() {
|
||||||
|
return actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Securable getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canRead() {
|
||||||
|
return can_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canWrite() {
|
||||||
|
return can_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canDelete() {
|
||||||
|
return can_delete;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canCreate() {
|
||||||
|
return can_create;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSecure() {
|
||||||
|
return can_secure;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean canExecute() {
|
||||||
|
return can_execute;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec.plain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.reliancy.jabba.sec.Securable;
|
||||||
|
import com.reliancy.jabba.sec.SecurityPermit;
|
||||||
|
import com.reliancy.jabba.sec.SecurityPolicy;
|
||||||
|
import com.reliancy.jabba.sec.SecurityStore;
|
||||||
|
import com.reliancy.util.Handy;
|
||||||
|
|
||||||
|
public class PlainSecurable implements Securable{
|
||||||
|
SecurityStore store;
|
||||||
|
Integer id;
|
||||||
|
Securable owner;
|
||||||
|
String kind;
|
||||||
|
String name;
|
||||||
|
String title;
|
||||||
|
String icon;
|
||||||
|
boolean essential;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityStore getStore() {
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
protected PlainSecurable setId(Integer id){
|
||||||
|
this.id=id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getKind() {
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
public PlainSecurable setKind(String v){
|
||||||
|
kind=v;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
public PlainSecurable setName(String v){
|
||||||
|
name=v;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return title!=null?title:Handy.prettyPrint(getName());
|
||||||
|
}
|
||||||
|
public PlainSecurable setTitle(String v){
|
||||||
|
title=v;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
public PlainSecurable setIcon(String v){
|
||||||
|
icon=v;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Securable getOwner() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Securable> getOwnedSecurables() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SecurityPermit> getDirectPermits() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityPermit getPermit(Securable sec) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityPolicy getPolicy() {
|
||||||
|
return store!=null?store.getPolicy():null;
|
||||||
|
}
|
||||||
|
public boolean isEssential() {
|
||||||
|
return essential;
|
||||||
|
}
|
||||||
|
public PlainSecurable setEssential(boolean essential) {
|
||||||
|
this.essential = essential;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.sec.plain;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.reliancy.dbo.Bag;
|
||||||
|
import com.reliancy.jabba.Path;
|
||||||
|
import com.reliancy.jabba.sec.NotPermitted;
|
||||||
|
import com.reliancy.jabba.sec.Securable;
|
||||||
|
import com.reliancy.jabba.sec.SecurityActor;
|
||||||
|
import com.reliancy.jabba.sec.SecurityPermit;
|
||||||
|
import com.reliancy.jabba.sec.SecurityPolicy;
|
||||||
|
import com.reliancy.jabba.sec.SecurityStore;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PlainSecurityStore is a container for plain security elements.
|
||||||
|
* It will implement a simple in-memory list of items. Optionally it will
|
||||||
|
* have be able to load or save its state to disk as json.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class PlainSecurityStore implements SecurityStore {
|
||||||
|
final Bag<PlainSecurable> securables=new Bag<PlainSecurable>();
|
||||||
|
final Bag<PlainPermit> permits=new Bag<PlainPermit>();
|
||||||
|
SecurityPolicy policy;
|
||||||
|
PlainActor guest;
|
||||||
|
PlainActor admin;
|
||||||
|
Path path;
|
||||||
|
|
||||||
|
public PlainSecurityStore() {
|
||||||
|
guest=(PlainActor) newActor();
|
||||||
|
guest.setPassword("").setName("guest").setEssential(true);
|
||||||
|
admin=(PlainActor) newActor();
|
||||||
|
admin.setPassword("admin").setName("admin").setTitle("Administrator").setEssential(true);
|
||||||
|
securables.add(guest);
|
||||||
|
securables.add(admin);
|
||||||
|
}
|
||||||
|
public PlainSecurityStore setPath(Path p){
|
||||||
|
path=p;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public void load() throws IOException{
|
||||||
|
|
||||||
|
}
|
||||||
|
public void save() throws IOException{
|
||||||
|
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Securable newSecurable() {
|
||||||
|
return new PlainSecurable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityActor newActor() {
|
||||||
|
return new PlainActor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityPermit newPermit() {
|
||||||
|
return new PlainPermit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteSecurable(SecurityActor actor, Securable sec) throws IOException {
|
||||||
|
if(policy!=null){
|
||||||
|
// if policy is set only admin level users can access actors via securable
|
||||||
|
if(actor!=admin && actor!=sec.getOwner()) throw new NotPermitted("admin or owner rights required");
|
||||||
|
}
|
||||||
|
securables.remove((PlainSecurable)sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveSecurable(SecurityActor actor, Securable sec) throws IOException {
|
||||||
|
if(policy!=null){
|
||||||
|
// if policy is set only admin level users can access actors via securable
|
||||||
|
if(actor!=admin && actor!=sec.getOwner()) throw new NotPermitted("admin or owner rights required");
|
||||||
|
}
|
||||||
|
securables.add((PlainSecurable)sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Securable loadSecurable(SecurityActor actor, Integer id) throws IOException, NotPermitted {
|
||||||
|
if(policy!=null){
|
||||||
|
// if policy is set only admin level users can access actors via securable
|
||||||
|
if(actor!=admin) throw new NotPermitted("admin rights required");
|
||||||
|
}
|
||||||
|
if(id==ADMIN) return admin;
|
||||||
|
if(id==GUEST) return guest;
|
||||||
|
for(Securable sec:securables){
|
||||||
|
if(sec.getId()==id) return sec;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityActor loadActor(SecurityActor actor, String name, String pwd) throws IOException, NotPermitted {
|
||||||
|
if(policy!=null){
|
||||||
|
// if policy is set only admin level users can access actors via securable
|
||||||
|
if(actor!=admin) throw new NotPermitted("admin rights required");
|
||||||
|
}
|
||||||
|
for(Securable sec:securables){
|
||||||
|
if(!(sec instanceof SecurityActor)) continue; // skip over non actors
|
||||||
|
SecurityActor a=(SecurityActor) sec;
|
||||||
|
if(!a.getName().equalsIgnoreCase(name)) continue; // name mismatch
|
||||||
|
if(pwd!=null && a.authPassword(pwd)==a) return a; // match on password if provided
|
||||||
|
boolean actor_permitted=actor==admin;
|
||||||
|
if(!actor_permitted) continue; // actor is not permitted
|
||||||
|
return a; // if permitted lookup by name role or user
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Securable> loadSecurables(SecurityActor actor, Securable sec) throws IOException, NotPermitted {
|
||||||
|
if(policy!=null){
|
||||||
|
// if policy is set only admin level users can access actors via securable
|
||||||
|
if(actor!=admin && actor!=sec.getOwner()) throw new NotPermitted("admin or owner rights required");
|
||||||
|
}
|
||||||
|
ArrayList<Securable> ret=new ArrayList<>();
|
||||||
|
for(Securable s:securables){
|
||||||
|
if(sec==s.getOwner()) ret.add(s);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deletePermit(SecurityActor actor, SecurityPermit permit) throws IOException {
|
||||||
|
if(policy!=null){
|
||||||
|
// if policy is set only admin level users can access actors via securable
|
||||||
|
if(actor!=admin) throw new NotPermitted("admin or owner rights required");
|
||||||
|
}
|
||||||
|
permits.remove((PlainPermit)permit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void savePermit(SecurityActor actor, SecurityPermit permit) throws IOException {
|
||||||
|
if(policy!=null){
|
||||||
|
// if policy is set only admin level users can access actors via securable
|
||||||
|
if(actor!=admin) throw new NotPermitted("admin or owner rights required");
|
||||||
|
}
|
||||||
|
permits.add((PlainPermit)permit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityPermit loadPermit(SecurityActor actor, Integer id) throws IOException, NotPermitted {
|
||||||
|
if(policy!=null){
|
||||||
|
// if policy is set only admin level users can access actors via securable
|
||||||
|
if(actor!=admin) throw new NotPermitted("admin rights required");
|
||||||
|
}
|
||||||
|
for(SecurityPermit p:permits){
|
||||||
|
if(p.getId()==id) return p;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public List<SecurityPermit> loadPermitsBy(SecurityActor actor, SecurityActor sec) throws IOException, NotPermitted {
|
||||||
|
if(policy!=null){
|
||||||
|
// if policy is set only admin level users can access actors via securable
|
||||||
|
if(actor!=admin) throw new NotPermitted("admin rights required");
|
||||||
|
}
|
||||||
|
ArrayList<SecurityPermit> ret=new ArrayList<>();
|
||||||
|
for(SecurityPermit p:permits){
|
||||||
|
if(p.getActor()==sec) ret.add(p);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public List<SecurityPermit> loadPermitsOn(SecurityActor actor, Securable sec) throws IOException, NotPermitted {
|
||||||
|
if(policy!=null){
|
||||||
|
// if policy is set only admin level users can access actors via securable
|
||||||
|
if(actor!=admin) throw new NotPermitted("admin rights required");
|
||||||
|
}
|
||||||
|
ArrayList<SecurityPermit> ret=new ArrayList<>();
|
||||||
|
for(SecurityPermit p:permits){
|
||||||
|
if(p.getSubject()==sec) ret.add(p);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPolicy(SecurityPolicy policy) {
|
||||||
|
if(this.policy!=null) throw new IllegalStateException("Store is locked already.");
|
||||||
|
this.policy=policy;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public SecurityPolicy getPolicy() {
|
||||||
|
return this.policy;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.reliancy.jabba.ui;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
import com.reliancy.jabba.AppSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of Feedback events with siphon like iterator.
|
||||||
|
* When iterating over the list it pops values as well.
|
||||||
|
*/
|
||||||
|
public class Feedback extends LinkedList<FeedbackLine>{
|
||||||
|
public static Feedback get(){
|
||||||
|
AppSession ass=AppSession.getInstance();
|
||||||
|
return ass!=null?ass.getFeedback():null;
|
||||||
|
}
|
||||||
|
class Siphon implements Iterator<FeedbackLine>{
|
||||||
|
final ListIterator<FeedbackLine> backend;
|
||||||
|
public Siphon(ListIterator<FeedbackLine> it){
|
||||||
|
backend=it;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return backend.hasNext();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public FeedbackLine next() {
|
||||||
|
FeedbackLine ret=backend.next();
|
||||||
|
backend.remove();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Iterator<FeedbackLine> iterator(){
|
||||||
|
return new Siphon(this.listIterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.ui;
|
||||||
|
/**
|
||||||
|
* Message object to inform users about things.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FeedbackLine {
|
||||||
|
public static final String ERROR="danger";
|
||||||
|
public static final String WARN="warning";
|
||||||
|
public static final String INFO="info";
|
||||||
|
String type;
|
||||||
|
String message;
|
||||||
|
public FeedbackLine(String typ,String message){
|
||||||
|
this.type=typ;
|
||||||
|
this.message=message;
|
||||||
|
}
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
public static FeedbackLine error(String message){
|
||||||
|
return new FeedbackLine(ERROR, message);
|
||||||
|
}
|
||||||
|
public static FeedbackLine warn(String message){
|
||||||
|
return new FeedbackLine(WARN, message);
|
||||||
|
}
|
||||||
|
public static FeedbackLine info(String message){
|
||||||
|
return new FeedbackLine(INFO, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.ui;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Helper or model class to manage menus and toolbars.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Menu extends MenuItem{
|
||||||
|
public static final String TOP="/menu/top";
|
||||||
|
public static final String LEFT="/menu/left";
|
||||||
|
|
||||||
|
public static final HashMap<String,Menu> menus=new HashMap<>();
|
||||||
|
public static void publish(String name,Menu m){
|
||||||
|
menus.put(name,m);
|
||||||
|
}
|
||||||
|
public static Menu request(String name){
|
||||||
|
Menu ret=menus.get(name);
|
||||||
|
if(ret==null){
|
||||||
|
ret=new Menu(name);
|
||||||
|
publish(name,ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
final ArrayList<MenuItem> items=new ArrayList<>();
|
||||||
|
public Menu(String id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
public int getSize(){
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
public Menu add(MenuItem itm){
|
||||||
|
items.add(itm);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Menu addSpacer(){
|
||||||
|
return add(new MenuItem("###"));
|
||||||
|
}
|
||||||
|
public MenuItem find(String id){
|
||||||
|
if(id.equalsIgnoreCase(getId())) return this;
|
||||||
|
for(MenuItem itm:items) if(id.equalsIgnoreCase(itm.getId())) return itm;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public List<MenuItem> getItems() {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
public Iterable<MenuItem> getBefore() {
|
||||||
|
final ArrayList<MenuItem> ret=new ArrayList<>();
|
||||||
|
for(MenuItem itm:items){
|
||||||
|
if("###".equals(itm.getId())) break;
|
||||||
|
ret.add(itm);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
public Iterable<MenuItem> getAfter() {
|
||||||
|
final ArrayList<MenuItem> ret=new ArrayList<>();
|
||||||
|
boolean adding=false;
|
||||||
|
for(MenuItem itm:items){
|
||||||
|
if("###".equals(itm.getId())){
|
||||||
|
adding=true;
|
||||||
|
}else if(adding){
|
||||||
|
ret.add(itm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.ui;
|
||||||
|
|
||||||
|
import com.reliancy.util.Handy;
|
||||||
|
|
||||||
|
/** Individual menu items within a menu.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MenuItem {
|
||||||
|
final String id;
|
||||||
|
String title;
|
||||||
|
String url;
|
||||||
|
String icon;
|
||||||
|
public MenuItem(String id) {
|
||||||
|
this.id=id;
|
||||||
|
this.title=Handy.prettyPrint(id);
|
||||||
|
this.url="/"+id;
|
||||||
|
}
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public int getSize(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
public MenuItem setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
public MenuItem setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public String getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
public MenuItem setIcon(String icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.ui;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.github.jknack.handlebars.Context;
|
||||||
|
import com.reliancy.util.CodeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data context and render task for our handlebars UI system.
|
||||||
|
* We create a rendering for a template, load it with data and then flush it at end into a document.
|
||||||
|
* In this class we also inject some global variables such as menu, feedback and user.
|
||||||
|
*/
|
||||||
|
public class Rendering extends HashMap<String,Object>{
|
||||||
|
public static Rendering begin(Template t){
|
||||||
|
Rendering ret=new Rendering(t);
|
||||||
|
ret.with("menu",Menu.request(Menu.TOP));
|
||||||
|
ret.with("toolbar",Menu.request(Menu.LEFT));
|
||||||
|
ret.with("feedback",Feedback.get());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
public static Rendering begin(String path,Object ...sp){
|
||||||
|
//if(sp==null ||sp.length==0) sp=new Object[]{"./var",App.class};
|
||||||
|
Template t=Template.find(path,sp);
|
||||||
|
if(t==null) throw new CodeException(Template.ERR_BADTEMPLATE).put("template",path);
|
||||||
|
return begin(t);
|
||||||
|
}
|
||||||
|
Context ctx;
|
||||||
|
Template template;
|
||||||
|
public Rendering(Template t){
|
||||||
|
ctx=Context.newBuilder(this).build();
|
||||||
|
template=t;
|
||||||
|
}
|
||||||
|
public CharSequence end() throws IOException{
|
||||||
|
try{
|
||||||
|
CharSequence ret=template.render(ctx);
|
||||||
|
return ret;
|
||||||
|
}finally{
|
||||||
|
ctx.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void end(Writer _out) throws IOException{
|
||||||
|
try{
|
||||||
|
template.render(ctx,_out);
|
||||||
|
}finally{
|
||||||
|
ctx.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Rendering with(String key,Object val){
|
||||||
|
put(key,val);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Rendering with(Map<String,?> kv){
|
||||||
|
for(Map.Entry<String,?> e:kv.entrySet()){
|
||||||
|
put(e.getKey(),e.getValue());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Rendering with(Throwable ex){
|
||||||
|
StringBuilder msg=new StringBuilder();
|
||||||
|
StringBuilder title=new StringBuilder();
|
||||||
|
CodeException.fillUserMessage(ex, msg, title);
|
||||||
|
with("error_title",title.toString());
|
||||||
|
with("error_message",msg);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+33
-7
@@ -1,16 +1,27 @@
|
|||||||
package com.reliancy.util;
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.jabba.ui;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import com.github.jknack.handlebars.Handlebars;
|
import com.github.jknack.handlebars.Handlebars;
|
||||||
|
import com.github.jknack.handlebars.helper.StringHelpers;
|
||||||
import com.github.jknack.handlebars.io.AbstractTemplateLoader;
|
import com.github.jknack.handlebars.io.AbstractTemplateLoader;
|
||||||
import com.github.jknack.handlebars.io.TemplateSource;
|
import com.github.jknack.handlebars.io.TemplateSource;
|
||||||
import com.github.jknack.handlebars.io.URLTemplateSource;
|
import com.github.jknack.handlebars.io.URLTemplateSource;
|
||||||
|
import com.reliancy.util.Resources;
|
||||||
|
import com.reliancy.util.ResultCode;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
import com.hubspot.jinjava.Jinjava;
|
import com.hubspot.jinjava.Jinjava;
|
||||||
@@ -49,17 +60,24 @@ public class Template {
|
|||||||
public TemplateSource sourceAt(String location) throws IOException {
|
public TemplateSource sourceAt(String location) throws IOException {
|
||||||
String fullpath=this.resolve(location);
|
String fullpath=this.resolve(location);
|
||||||
URL loc=Resources.findFirst(null,fullpath,Template.search_path);
|
URL loc=Resources.findFirst(null,fullpath,Template.search_path);
|
||||||
System.out.println(location+":"+loc+":"+fullpath);
|
//System.out.println(location+":"+loc+":"+fullpath);
|
||||||
if (loc == null) {
|
if (loc == null) {
|
||||||
Logger.getLogger(Template.class.getSimpleName()).warning("Missing template"+fullpath);
|
Logger.getLogger(Template.class.getSimpleName()).warning("Missing template"+fullpath);
|
||||||
throw new FileNotFoundException(location);
|
throw new FileNotFoundException(location);
|
||||||
}
|
}
|
||||||
return new URLTemplateSource(location,loc);
|
return new URLTemplateSource(location,loc);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
static Handlebars handlebars;
|
||||||
|
static{
|
||||||
|
handlebars= new Handlebars(new HBLoader());
|
||||||
|
StringHelpers.register(handlebars);
|
||||||
|
/*
|
||||||
|
for(ConditionalHelpers h:ConditionalHelpers.values()){
|
||||||
|
|
||||||
}
|
}
|
||||||
static Handlebars handlebars = new Handlebars(new HBLoader());
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
static Object[] search_path;
|
static Object[] search_path;
|
||||||
static HashMap<String,Template> cache=new HashMap<>();
|
static HashMap<String,Template> cache=new HashMap<>();
|
||||||
@@ -94,6 +112,7 @@ public class Template {
|
|||||||
if(sp!=null && sp.length>0) search_path=sp;
|
if(sp!=null && sp.length>0) search_path=sp;
|
||||||
return search_path;
|
return search_path;
|
||||||
}
|
}
|
||||||
|
public static final int ERR_BADTEMPLATE=ResultCode.defineFailure(0x01,Template.class,"bad template: ${template}");
|
||||||
com.github.jknack.handlebars.Template recipe;
|
com.github.jknack.handlebars.Template recipe;
|
||||||
final URL location;
|
final URL location;
|
||||||
String source;
|
String source;
|
||||||
@@ -114,13 +133,20 @@ public class Template {
|
|||||||
if(source==null) this.source=Resources.toString(location);
|
if(source==null) this.source=Resources.toString(location);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public CharSequence render(Map<String,?> context) throws IOException{
|
public CharSequence render(Object context) throws IOException{
|
||||||
if(source==null) load();
|
if(source==null) load();
|
||||||
//String ret = jinjava.render(source, context);
|
//String ret = jinjava.render(source, context);
|
||||||
if(recipe==null){
|
if(recipe==null){
|
||||||
recipe=handlebars.compileInline(source);
|
recipe=handlebars.compileInline(source);
|
||||||
}
|
}
|
||||||
String ret=recipe.apply(context);
|
return recipe.apply(context);
|
||||||
return ret;
|
}
|
||||||
|
public void render(Object context,Writer _out) throws IOException{
|
||||||
|
if(source==null) load();
|
||||||
|
//String ret = jinjava.render(source, context);
|
||||||
|
if(recipe==null){
|
||||||
|
recipe=handlebars.compileInline(source);
|
||||||
|
}
|
||||||
|
recipe.apply(context,_out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.reliancy.jabbasec;
|
|
||||||
|
|
||||||
public class NotAuthentic extends RuntimeException {
|
|
||||||
public NotAuthentic(String message){
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
public NotAuthentic(String message,Throwable cause){
|
|
||||||
super(message,cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.reliancy.jabbasec;
|
|
||||||
/**
|
|
||||||
* Our own exception to throw when we are not allowed access.
|
|
||||||
*/
|
|
||||||
public class NotPermitted extends RuntimeException{
|
|
||||||
public NotPermitted(String message){
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
public NotPermitted(String message,Throwable cause){
|
|
||||||
super(message,cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package com.reliancy.jabbasec;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Any entity that can be secured or permissions set for an actor.
|
|
||||||
*/
|
|
||||||
public interface Securable {
|
|
||||||
public Integer getId();
|
|
||||||
public Integer getOwnerId();
|
|
||||||
public String getKind();
|
|
||||||
public String getName();
|
|
||||||
public String getTitle();
|
|
||||||
public String getIcon();
|
|
||||||
public Securable getOwner();
|
|
||||||
public List<Securable> getOwnedSecurables();
|
|
||||||
public List<SecurityPermit> getDirectPermits();
|
|
||||||
public SecurityPermit getPermit(Securable sec);
|
|
||||||
public SecurityPolicy getPolicy();
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package com.reliancy.jabbasec;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface that is implemented by any User or Principal entity.
|
|
||||||
* Often an AppSession will be determined in many ways by the user.
|
|
||||||
*/
|
|
||||||
public interface SecurityActor extends Securable{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
package com.reliancy.jabbasec;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.reliancy.jabba.AppSession;
|
|
||||||
import com.reliancy.jabba.CallSession;
|
|
||||||
import com.reliancy.jabba.Processor;
|
|
||||||
import com.reliancy.jabba.Request;
|
|
||||||
import com.reliancy.jabba.Response;
|
|
||||||
import com.reliancy.util.Handy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SecurityPolicy is a filter/processor that implements various auth protocols but also sources users.
|
|
||||||
* The policy will produce new users once they are authenticated.
|
|
||||||
* SecurityPolicy will authenticate SecurityActors and possibly authorize them via a permission mechanism
|
|
||||||
* to access.
|
|
||||||
* SecurityProtocol is one authenticatio method. We will not adjust response only try to recover user.
|
|
||||||
* Initialization of auth will occur outside:
|
|
||||||
* - for gui after login we will set an auth cookie to remember user and password
|
|
||||||
* - for APIs if user is required will issue error 401 with WWW-Authenticate header
|
|
||||||
*/
|
|
||||||
public class SecurityPolicy extends Processor{
|
|
||||||
public static final String KEY_NAME="jbauth";
|
|
||||||
protected String secret="sdfklgj 7150 9178-54=09";
|
|
||||||
protected ArrayList<SecurityProtocol> protocols;
|
|
||||||
protected SecurityActor admin;
|
|
||||||
protected SecurityActor guest;
|
|
||||||
|
|
||||||
public SecurityPolicy() {
|
|
||||||
super(SecurityPolicy.class.getSimpleName().toLowerCase());
|
|
||||||
protocols=new ArrayList<>();
|
|
||||||
protocols.add(new SecurityProtocol.Digest());
|
|
||||||
protocols.add(new SecurityProtocol.Basic());
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void before(Request request, Response response) throws IOException {
|
|
||||||
// we will recover a user here
|
|
||||||
CallSession css=CallSession.getInstance();
|
|
||||||
AppSession ass=(AppSession) css.getAppSession();
|
|
||||||
if(ass==null || ass.getUser()!=null){
|
|
||||||
return; // we got a user all good
|
|
||||||
}
|
|
||||||
try{
|
|
||||||
SecurityActor user=authenticate(request);
|
|
||||||
if(user!=null) ass.setUser(user);
|
|
||||||
}catch(NotAuthentic ex){
|
|
||||||
// we could not establish user
|
|
||||||
response.setStatus(Response.HTTP_FORBIDDEN);
|
|
||||||
response.getEncoder().writeObject(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void after(Request request, Response response) throws IOException {
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void serve(Request request, Response response) throws IOException {
|
|
||||||
// nothing to do here
|
|
||||||
}
|
|
||||||
protected String getSecret(){
|
|
||||||
return secret;
|
|
||||||
}
|
|
||||||
/** authenticates or establishes user based on request and updates response.
|
|
||||||
* this method might redirect to a login view. once it is done
|
|
||||||
* @param req
|
|
||||||
* @param res
|
|
||||||
* @return user we could establish
|
|
||||||
* @throws NotPermitted
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public SecurityActor authenticate(Request req) throws IOException, NotAuthentic{
|
|
||||||
// must recover user from cookies or by redirecting to login
|
|
||||||
log().info("User is missing.");
|
|
||||||
String auth=req.getCookie(KEY_NAME,null);
|
|
||||||
if(auth!=null){
|
|
||||||
// we have an auth cookie - encoded user login
|
|
||||||
Map<String,String> kv=Handy.decrypt(getSecret(),auth);
|
|
||||||
String username=(kv.get("n"));
|
|
||||||
String password=(kv.get("p"));
|
|
||||||
String address=(kv.get("a"));
|
|
||||||
if(address!=null && !address.equals(req.getRemoteAddress())){
|
|
||||||
return null; // invalid auth cookie
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
SecurityActor user = loadActor(admin,username,password);
|
|
||||||
if(user!=null) return user;
|
|
||||||
else throw new NotAuthentic("invalid credentials");
|
|
||||||
} catch (NotPermitted e) {
|
|
||||||
throw new NotAuthentic("not permitted to authenticate",e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// try authorization header as fallback - can't clear it always
|
|
||||||
auth=req.getHeader("Authorization");
|
|
||||||
if(auth!=null){
|
|
||||||
String[] kv=auth.split(" ",2);
|
|
||||||
String proto_name=kv[0];
|
|
||||||
String proto_args=kv.length>1?kv[1]:"";
|
|
||||||
for(SecurityProtocol sproto:protocols){
|
|
||||||
if(proto_name.equalsIgnoreCase(sproto.getName())){
|
|
||||||
return sproto.authenticate(this, req,proto_args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new NotAuthentic("auth method not supported:"+proto_name);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/** will establish what if any rights an actor has on a securable. */
|
|
||||||
public SecurityPermit authorize(SecurityActor actor, Securable subject){
|
|
||||||
return actor.getPermit(subject);
|
|
||||||
}
|
|
||||||
/** loads a securable by id given actor permits. */
|
|
||||||
public Securable loadSecurable(SecurityActor actor, Integer id) throws IOException, NotPermitted{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/** loads an actor given name and password. */
|
|
||||||
public SecurityActor loadActor(SecurityActor actor, String name, String pwd) throws IOException, NotPermitted{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/** will save a securable including a user if permitted via actor. */
|
|
||||||
public void saveSecurable(SecurityActor actor, Securable sec) throws IOException{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package com.reliancy.jabbasec;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.reliancy.jabba.Request;
|
|
||||||
import com.reliancy.util.Handy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A SecurityProtocol will be processing HTTP to establish SecurityActor or user.
|
|
||||||
*/
|
|
||||||
public abstract class SecurityProtocol {
|
|
||||||
protected final String name;
|
|
||||||
public SecurityProtocol(String n){
|
|
||||||
name=n;
|
|
||||||
}
|
|
||||||
public String getName(){
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
public abstract SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) throws NotPermitted, IOException ;
|
|
||||||
public static class Basic extends SecurityProtocol{
|
|
||||||
public Basic(){
|
|
||||||
super("Basic");
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) throws NotPermitted, IOException {
|
|
||||||
tok=String.valueOf(Handy.decodeBase64(tok)) ;
|
|
||||||
String[] up=tok.split(":",2);
|
|
||||||
String userid=up[0];
|
|
||||||
String pwd=up.length>1?up[1]:"";
|
|
||||||
return policy.loadActor(policy.admin,userid,pwd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static class Digest extends SecurityProtocol{
|
|
||||||
public Digest(){
|
|
||||||
super("Digest");
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.reliancy.rec;
|
package com.reliancy.rec;
|
||||||
|
|
||||||
/** Similar to a SAX interface used by parsers for XML and JSON to assemble DOM structures.
|
/** Similar to a SAX interface used by parsers for XML and JSON to assemble DOM structures.
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.rec;
|
package com.reliancy.rec;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.rec;
|
package com.reliancy.rec;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
*/
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.reliancy.rec;
|
package com.reliancy.rec;
|
||||||
|
|
||||||
@@ -12,7 +14,7 @@ import com.reliancy.util.Tokenizer;
|
|||||||
import com.reliancy.util.Handy;
|
import com.reliancy.util.Handy;
|
||||||
|
|
||||||
/** Special class which will tokenize string according to rules for JSON and feed the info to a listener.
|
/** Special class which will tokenize string according to rules for JSON and feed the info to a listener.
|
||||||
*
|
* TODO: reuse headers in an array if same structure
|
||||||
* @author amer
|
* @author amer
|
||||||
*/
|
*/
|
||||||
public class JSONDecoder implements TextDecoder,DecoderSink {
|
public class JSONDecoder implements TextDecoder,DecoderSink {
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.rec;
|
package com.reliancy.rec;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.rec;
|
package com.reliancy.rec;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.rec;
|
package com.reliancy.rec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.rec;
|
package com.reliancy.rec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
*/
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.reliancy.rec;
|
package com.reliancy.rec;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.rec;
|
package com.reliancy.rec;
|
||||||
/**
|
/**
|
||||||
* dimensioned container of values.
|
* dimensioned container of values.
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.reliancy.util;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/** One exception to rule them all.
|
||||||
|
* This exception works with ResultCode and represents and instance with context information.
|
||||||
|
* If a ResultCode is deemed parametric then we use provided parameters to update it when generating a message.
|
||||||
|
*
|
||||||
|
* @author amer
|
||||||
|
*/
|
||||||
|
public class CodeException extends RuntimeException {
|
||||||
|
|
||||||
|
protected final int code;
|
||||||
|
protected final HashMap<String,Object> context=new HashMap<>();
|
||||||
|
public CodeException(int code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
public CodeException(Throwable cause, int code) {
|
||||||
|
super(cause);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
return getMessage();
|
||||||
|
}
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
public ResultCode getResultCode(){
|
||||||
|
return ResultCode.get(code);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
ResultCode rcode=getResultCode();
|
||||||
|
if(rcode!=null){
|
||||||
|
boolean wrapped=(rcode.getCode()==ResultCode.FAILURE);
|
||||||
|
String msg=rcode.getMessage();
|
||||||
|
if(msg.contains("$")){
|
||||||
|
for(String key:context.keySet()){
|
||||||
|
Object obj=context.get(key);
|
||||||
|
if(obj==null) continue;
|
||||||
|
String val=String.valueOf(obj);
|
||||||
|
msg=msg.replaceAll("\\$\\{"+key+"\\}",val);
|
||||||
|
}
|
||||||
|
}else if(this.getCause()!=null && wrapped){
|
||||||
|
msg=CodeException.getUserMessage(this.getCause());
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}else{
|
||||||
|
return "("+String.format("%08X", code)+")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(String name) {
|
||||||
|
return (T)context.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeException put(String name, String value) {
|
||||||
|
context.put(name, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public static CodeException wrap(Throwable exception) {
|
||||||
|
if (exception instanceof CodeException) {
|
||||||
|
CodeException se = (CodeException)exception;
|
||||||
|
return se;
|
||||||
|
} else {
|
||||||
|
return new CodeException(exception,ResultCode.FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static String getUserMessage(Throwable ex,Object context) {
|
||||||
|
return getUserMessage(ex);
|
||||||
|
}
|
||||||
|
public static String getUserMessage(Throwable ex) {
|
||||||
|
StringBuilder buf=new StringBuilder();
|
||||||
|
fillUserMessage(ex,buf,null);
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
public static Throwable fillUserMessage(Throwable ex,StringBuilder msg,StringBuilder title) {
|
||||||
|
Throwable c = ex;
|
||||||
|
System.out.println(">>>"+c+"/"+c.getCause());
|
||||||
|
while(c.getCause()!=null){
|
||||||
|
Throwable cc= c.getCause();
|
||||||
|
if(c.getMessage()==null){
|
||||||
|
c=cc;continue;
|
||||||
|
}
|
||||||
|
String cMsg=c.getMessage();
|
||||||
|
String ccMsg=cc.getMessage();
|
||||||
|
System.out.println("!!!"+cMsg+"/"+c.getClass().getName()+"/"+cc.getClass().getName());
|
||||||
|
boolean wrapped=(c instanceof CodeException) && ((CodeException)c).getCode()==ResultCode.FAILURE;
|
||||||
|
boolean plain_at=cMsg.equals(c.getClass().getName());
|
||||||
|
boolean plain_sub=cMsg.equals(cc.getClass().getName());
|
||||||
|
boolean same_msg=cMsg.equalsIgnoreCase(ccMsg);
|
||||||
|
System.out.println("\t"+plain_sub+"#"+cc+"$"+cc.getCause()+"*"+cc.getMessage());
|
||||||
|
if(plain_at || plain_sub || cMsg.startsWith(cc.getClass().getName()+":") || same_msg || wrapped){
|
||||||
|
c=cc;
|
||||||
|
}else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("CC:"+c);
|
||||||
|
// take care of title
|
||||||
|
String _title=c.getClass().getSimpleName();
|
||||||
|
if(c instanceof CodeException){
|
||||||
|
CodeException cc=(CodeException) c;
|
||||||
|
if(cc.getCause()!=null){
|
||||||
|
_title=cc.getClass().getSimpleName();
|
||||||
|
}else{
|
||||||
|
// we do not have a cause
|
||||||
|
int code=cc.getCode();
|
||||||
|
ResultCode rcode=ResultCode.get(code);
|
||||||
|
if(rcode!=null) _title=rcode.getSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(title!=null) title.append(_title);
|
||||||
|
// now take care of detail
|
||||||
|
String _msg=c.getLocalizedMessage();
|
||||||
|
if(_msg==null || _msg.trim().isEmpty()){
|
||||||
|
_msg=c.getClass().getSimpleName();
|
||||||
|
StackTraceElement[] se=c.getStackTrace();
|
||||||
|
if(se!=null && se.length>0) _msg+="\n\t at "+se[0].toString();
|
||||||
|
}
|
||||||
|
String prefString="Exception:";
|
||||||
|
String prefString2="Error:";
|
||||||
|
int prefix=_msg.lastIndexOf(prefString);
|
||||||
|
if(prefix<0) prefix=_msg.lastIndexOf(prefString2);
|
||||||
|
if(prefix>0 && _msg.substring(0, prefix).contains(".")) _msg=_msg.substring(prefix+prefString.length());
|
||||||
|
if(msg!=null) msg.append(_msg);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.util;
|
package com.reliancy.util;
|
||||||
/**
|
|
||||||
* Common utility methods.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -19,18 +23,56 @@ import java.util.zip.DataFormatException;
|
|||||||
import java.util.zip.Deflater;
|
import java.util.zip.Deflater;
|
||||||
import java.util.zip.Inflater;
|
import java.util.zip.Inflater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common utility methods.
|
||||||
|
*/
|
||||||
public final class Handy {
|
public final class Handy {
|
||||||
|
public static final String WHITE=" \t\r\f\n";
|
||||||
|
/** place left-right around verb.*/
|
||||||
public static String wrap(String verb, String left, String right) {
|
public static String wrap(String verb, String left, String right) {
|
||||||
if(verb==null) verb="";
|
if(verb==null) verb="";
|
||||||
if(verb.startsWith(left) && verb.endsWith(right)) return verb;
|
if(verb.startsWith(left) && verb.endsWith(right)) return verb;
|
||||||
return left+verb.trim()+right;
|
return left+verb.trim()+right;
|
||||||
}
|
}
|
||||||
|
/** remove left-right around verb.*/
|
||||||
public static String unwrap(String verb, String left, String right) {
|
public static String unwrap(String verb, String left, String right) {
|
||||||
if(verb==null) return verb;
|
if(verb==null) return verb;
|
||||||
String ret=verb.trim();
|
String ret=verb.trim();
|
||||||
if(verb.startsWith(left) && verb.endsWith(right)) ret=verb.substring(1,verb.length()-1);
|
if(verb.startsWith(left) && verb.endsWith(right)) ret=verb.substring(1,verb.length()-1);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
/** remove any chars elements from left of verb. */
|
||||||
|
public static String trimLeft(String verb,String chars){
|
||||||
|
while(verb.length()>0 && chars.indexOf(verb.charAt(0))!=-1){
|
||||||
|
verb=verb.substring(1);
|
||||||
|
}
|
||||||
|
return verb;
|
||||||
|
}
|
||||||
|
/** remove any chars elements from right of verb. */
|
||||||
|
public static String trimRight(String verb,String chars){
|
||||||
|
while(verb.length()>0 && chars.indexOf(verb.charAt(verb.length()-1))!=-1){
|
||||||
|
verb=verb.substring(0,verb.length()-1);
|
||||||
|
}
|
||||||
|
return verb;
|
||||||
|
}
|
||||||
|
/** remove any chars elements from right and right of verb. */
|
||||||
|
public static String trimBoth(String verb,String chars){
|
||||||
|
verb=trimLeft(verb, chars);
|
||||||
|
verb=trimRight(verb, chars);
|
||||||
|
return verb;
|
||||||
|
}
|
||||||
|
/** remove any chars elements from right and right of verb symetrically. trims whitespace first. */
|
||||||
|
public static String trimEvenly(String verb,String chars){
|
||||||
|
verb=trimBoth(verb," \t\n\r\f");
|
||||||
|
while(verb.length()>1){
|
||||||
|
char left=verb.charAt(0);
|
||||||
|
char right=verb.charAt(verb.length()-1);
|
||||||
|
if(left!=right) break; // left-right not even
|
||||||
|
if(chars.indexOf(left)<0) break; // even but not in chars list
|
||||||
|
verb=verb.substring(1,verb.length()-1);
|
||||||
|
}
|
||||||
|
return verb;
|
||||||
|
}
|
||||||
public static <T> T nz(T val, T def){
|
public static <T> T nz(T val, T def){
|
||||||
return val!=null?val:def;
|
return val!=null?val:def;
|
||||||
}
|
}
|
||||||
@@ -219,7 +261,7 @@ public final class Handy {
|
|||||||
return bufs.toString();
|
return bufs.toString();
|
||||||
}
|
}
|
||||||
/** Attempts to take a user string and compact it to camel case.
|
/** Attempts to take a user string and compact it to camel case.
|
||||||
* @param str nicely formatted string
|
* @param value more or less presentable string
|
||||||
* @return nicely compact string
|
* @return nicely compact string
|
||||||
*/
|
*/
|
||||||
public static String toCamelCase(String value) {
|
public static String toCamelCase(String value) {
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* To change this template, choose Tools | Templates
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.reliancy.util;
|
package com.reliancy.util;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
@@ -13,7 +17,7 @@ import java.net.URLDecoder;
|
|||||||
* It might but should not hold any handles. It holds the address and
|
* It might but should not hold any handles. It holds the address and
|
||||||
* possibly takes care of looking up addresses.
|
* possibly takes care of looking up addresses.
|
||||||
* The richest syntax is:
|
* The richest syntax is:
|
||||||
* PROTOCOL://USER:PWD@MACHINE:PORT/DATABASE?key=val&...
|
* <pre> {@code PROTOCOL://USER:PWD@MACHINE:PORT/DATABASE?key=val&... } </pre>
|
||||||
* Properties are held in their own string and need to be decoded.
|
* Properties are held in their own string and need to be decoded.
|
||||||
*
|
*
|
||||||
* We use forward slash for path delimitation of database portion. For the rest we preserve other slashes to allow windows domain\\user or server\\instance.
|
* We use forward slash for path delimitation of database portion. For the rest we preserve other slashes to allow windows domain\\user or server\\instance.
|
||||||
@@ -380,7 +384,7 @@ public class Path {
|
|||||||
* method will split paths used in linux and windows.
|
* method will split paths used in linux and windows.
|
||||||
* in particular for windows it checks if a single letter precedes a colon in which case it considers it a volume
|
* in particular for windows it checks if a single letter precedes a colon in which case it considers it a volume
|
||||||
* and does not split there.
|
* and does not split there.
|
||||||
* @param paths paths joined with colon or semi-colon
|
* @param _paths paths joined with colon or semi-colon
|
||||||
* @return array of paths
|
* @return array of paths
|
||||||
*/
|
*/
|
||||||
public static String[] splitPaths(String _paths){
|
public static String[] splitPaths(String _paths){
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.reliancy.util;
|
package com.reliancy.util;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
package com.reliancy.util;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/** Utility class to handle error codes and error messages.
|
||||||
|
* Error codes are integers that describe outcome of an operation.
|
||||||
|
*
|
||||||
|
* In cases when return codes are used and not exceptions thrown it is a mess to keep track of what they mean.
|
||||||
|
* With this class we define a uniform way of managing return codes and allow for additional information.
|
||||||
|
* This additional information can be text that could be localized and give better user information about what happened.
|
||||||
|
*
|
||||||
|
* First we distinguish between success and failure. Any code that is negative is failure.
|
||||||
|
* Default success code is 0 and provides no additional info. Any positive code is a warning or info and possibly carries extra meaning and
|
||||||
|
* or description.
|
||||||
|
*
|
||||||
|
* success,failure,pending
|
||||||
|
* source
|
||||||
|
* parametric
|
||||||
|
*/
|
||||||
|
public class ResultCode {
|
||||||
|
public static final byte TYPE_SUCCESS=0x0;
|
||||||
|
public static final byte TYPE_PENDING=0x1;
|
||||||
|
public static final byte TYPE_FAILURE=0xF;
|
||||||
|
final int code;
|
||||||
|
final String message;
|
||||||
|
final String source;
|
||||||
|
public ResultCode(byte type,short value,String src,String message) {
|
||||||
|
this.message=message;
|
||||||
|
this.source=src;
|
||||||
|
this.code=ResultCode.getCode(type,value,source!=null?source.hashCode():0);
|
||||||
|
}
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getType() {
|
||||||
|
return getType(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return getValue(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
int code=getCode();
|
||||||
|
String context=getSource();
|
||||||
|
String message=getMessage();
|
||||||
|
if(context!=null){
|
||||||
|
return context+"("+String.format("%08X", code)+"):"+message;
|
||||||
|
}else{
|
||||||
|
return "("+String.format("%08X", code)+"):"+message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected static final HashMap<Integer,ResultCode> codes=new HashMap<>();
|
||||||
|
public static final int getCode(byte type,int value,int source){
|
||||||
|
int st=(type <<28) & 0xF0000000;
|
||||||
|
int sc=(source <<8) & 0x0FFFFF00;
|
||||||
|
int vl=(value) & 0x000000FF;
|
||||||
|
return (int) (st | sc | vl);
|
||||||
|
}
|
||||||
|
public static final byte getType(int code){
|
||||||
|
return (byte)((code>>28) & 0x0F);
|
||||||
|
}
|
||||||
|
public static final boolean testType(int code,byte st){
|
||||||
|
return getType(code)==st;
|
||||||
|
}
|
||||||
|
public static final boolean isSuccess(int code){
|
||||||
|
return testType(code,TYPE_SUCCESS);
|
||||||
|
}
|
||||||
|
public static final boolean isFailure(int code,int st){
|
||||||
|
return testType(code,TYPE_FAILURE);
|
||||||
|
}
|
||||||
|
public static final boolean isPending(int code,int st){
|
||||||
|
return testType(code,TYPE_PENDING);
|
||||||
|
}
|
||||||
|
public static final int getValue(int code){
|
||||||
|
return (int)(code & 0x000000FF);
|
||||||
|
}
|
||||||
|
public static final int getSource(int code){
|
||||||
|
return (int)((code & 0x0FFFFF00)>>8);
|
||||||
|
}
|
||||||
|
public static final synchronized ResultCode get(int code){
|
||||||
|
if(codes==null) return null;
|
||||||
|
return (ResultCode)codes.get(code);
|
||||||
|
}
|
||||||
|
public static final synchronized ResultCode put(ResultCode c){
|
||||||
|
ResultCode old=(ResultCode) codes.get(c.getCode());
|
||||||
|
codes.put(c.getCode(),c);
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
public static final int define(byte type,int value,Class<?> source,String message){
|
||||||
|
return define(type,value,source!=null?source.getSimpleName():null,message);
|
||||||
|
}
|
||||||
|
public static final int define(byte type,int value,String source,String message){
|
||||||
|
int code=getCode(type,value,source!=null?source.hashCode():0);
|
||||||
|
ResultCode c=get(code);
|
||||||
|
if(c!=null){
|
||||||
|
System.err.println("Result code redefinition(consider different value or source):"+c);
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
c=new ResultCode(type, (short) value,source,message);
|
||||||
|
put(c);
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
public static final int defineSuccess(int value,Class<?> source,String message){
|
||||||
|
return define(TYPE_SUCCESS,value,source,message);
|
||||||
|
}
|
||||||
|
public static final int defineFailure(int value,Class<?> source,String message){
|
||||||
|
return define(TYPE_FAILURE,value,source,message);
|
||||||
|
}
|
||||||
|
public static final int definePending(int value,Class<?> source,String message){
|
||||||
|
return define(TYPE_PENDING,value,source,message);
|
||||||
|
}
|
||||||
|
public static final int SUCCESS=ResultCode.defineSuccess(0,null,"Success");
|
||||||
|
public static final int FAILURE=ResultCode.defineFailure(0,null,"Failure");
|
||||||
|
public static final int PENDING=ResultCode.definePending(0,null,"Pending");
|
||||||
|
}
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
package com.reliancy.util;
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.reliancy.util;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{{#partial "MainSection"}}
|
||||||
|
<div class="row justify-content-center mt-3">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header alert alert-danger">
|
||||||
|
<h5 class="card-title"><i class="fas fa-exclamation-circle fa-2x"></i> {{error_title}}</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<!--<h5 class="card-title">Special title treatment</h5>-->
|
||||||
|
<p class="card-text">
|
||||||
|
{{error_message}}
|
||||||
|
</p>
|
||||||
|
<!--<a href="#" class="btn btn-primary">Go somewhere</a>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{{/partial}}
|
||||||
|
{{> frame-1c}}
|
||||||
@@ -1,4 +1,24 @@
|
|||||||
{{#partial "content"}}
|
{{#partial "MainSection"}}
|
||||||
Calling base
|
<div class="container col-fixed">
|
||||||
|
<form class="form-signin" method="POST">
|
||||||
|
<!--
|
||||||
|
<img class="mb-4" src="https://getbootstrap.com/docs/4.0/assets/brand/bootstrap-solid.svg" alt="" width="72" height="72">
|
||||||
|
-->
|
||||||
|
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
|
||||||
|
<label for="userid" class="sr-only">Email address</label>
|
||||||
|
<input id="userid" name="userid" class="form-control" placeholder="Email address" required autofocus>
|
||||||
|
<label for="password" class="sr-only">Password</label>
|
||||||
|
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required>
|
||||||
|
<div class="checkbox mb-3">
|
||||||
|
<!--
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="" value="remember-me"> Remember me
|
||||||
|
</label>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
|
||||||
|
<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
{{/partial}}
|
{{/partial}}
|
||||||
{{> base}}
|
{{> frame-1c}}
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.dbo;
|
package com.reliancy.dbo;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.sql.Timestamp;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import com.reliancy.rec.JSON;
|
import com.reliancy.rec.JSON;
|
||||||
@@ -93,25 +99,38 @@ public class TerminalTest {
|
|||||||
@Test
|
@Test
|
||||||
public void complexCRUD() throws IOException, SQLException{
|
public void complexCRUD() throws IOException, SQLException{
|
||||||
System.out.println("ComplexCRUD");
|
System.out.println("ComplexCRUD");
|
||||||
|
// Reading
|
||||||
try(Action act=t.begin().load(Product.class).execute()){
|
try(Action act=t.begin().load(Product.class).execute()){
|
||||||
for(DBO o:act){
|
for(DBO o:act){
|
||||||
System.out.println("DBO:"+o);
|
System.out.println("DBO:"+o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Saving
|
||||||
Product p=new Product();
|
Product p=new Product();
|
||||||
p.setStatus(DBO.Status.USED);
|
p.setStatus(DBO.Status.USED);
|
||||||
|
Product.id.set(p,35);
|
||||||
Product.kind.set(p,Product.class.getSimpleName());
|
Product.kind.set(p,Product.class.getSimpleName());
|
||||||
Product.name.set(p,"myproduct");
|
Product.name.set(p,"myproduct");
|
||||||
Product.created.set(p,new Date());
|
Product.created.set(p,new Date());
|
||||||
Product.short_info.set(p,"a sweet melody");
|
Product.short_info.set(p,"a sweet melody:"+new java.sql.Timestamp(System.currentTimeMillis()));
|
||||||
Product.display_name.set(p,"first entry");
|
Product.display_name.set(p,"first entry");
|
||||||
System.out.println("P0:"+JSON.toString(p));
|
System.out.println("Update P0:"+JSON.toString(p));
|
||||||
t.save(p);
|
t.save(p);
|
||||||
System.out.println("P1:"+JSON.toString(p));
|
System.out.println("Update P1:"+JSON.toString(p));
|
||||||
Product pp=t.load(Product.class, 35);
|
// Creating
|
||||||
|
Product pp=new Product();
|
||||||
|
Product.kind.set(pp,Product.class.getSimpleName());
|
||||||
|
Product.name.set(pp,"myproduct");
|
||||||
|
Product.created.set(pp,new Date());
|
||||||
|
Product.short_info.set(pp,"a sweet melody:");
|
||||||
|
Product.display_name.set(pp,"created entry:"+new java.sql.Timestamp(System.currentTimeMillis()));
|
||||||
|
t.save(pp);
|
||||||
|
System.out.println("Create PP0:"+JSON.toString(pp));
|
||||||
|
pp=t.load(Product.class, Product.id.get(pp,null));
|
||||||
System.out.println("Returning:"+pp);
|
System.out.println("Returning:"+pp);
|
||||||
//t.delete(pp);
|
// Deleting
|
||||||
Entity.retract(Maps.class);
|
t.delete(pp);
|
||||||
|
//Entity.retract(Maps.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
package com.reliancy.jabba;
|
package com.reliancy.jabba;
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -31,8 +38,9 @@ public class RouterTest
|
|||||||
{
|
{
|
||||||
//assertTrue( true );
|
//assertTrue( true );
|
||||||
System.out.println("Test router init...");
|
System.out.println("Test router init...");
|
||||||
Router r=new Router();
|
JettyApp r=new JettyApp();
|
||||||
RouterEndPoint rep=r.importEndPoints(r);
|
RoutedEndPoint rep=new RoutedEndPoint();
|
||||||
|
rep.importMethods(r);
|
||||||
rep.compile();
|
rep.compile();
|
||||||
//Matcher m=rep.match("GET","/helloPlain");
|
//Matcher m=rep.match("GET","/helloPlain");
|
||||||
Matcher m=rep.match("GET","/hello3/45");
|
Matcher m=rep.match("GET","/hello3/45");
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package com.reliancy.rec;
|
/*
|
||||||
import static org.junit.Assert.assertTrue;
|
Copyright (c) 2011-2022 Reliancy LLC
|
||||||
|
|
||||||
|
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||||
|
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||||
|
You may not use this file except in compliance with the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.reliancy.rec;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB |
Reference in New Issue
Block a user