prep to push

This commit is contained in:
Amer Agovic
2022-09-22 16:26:23 -05:00
parent d5e851c57d
commit a8e37f705d
90 changed files with 3159 additions and 747 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
arguments=
auto.sync=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=
eclipse.preferences.version=1
gradle.user.home=
+56
View File
@@ -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.
+12 -5
View File
@@ -1,15 +1,22 @@
# 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
* running a build via: gradle jar
* 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
* ~~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~~
* Auth middleware supporting basic and digest, an security entities
* ~~Auth middleware supporting basic and digest, and security entities~~
* ~~Static file serving~~
* ~~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
View File
@@ -11,8 +11,11 @@ apply plugin: 'application'
apply plugin: 'maven-publish'
group='com.reliancy'
mainClassName = group+'.'+name+'.JettyApp'
version = '0.1'
mainClassName = group+'.'+name+'.Router'
sourceCompatibility = 1.8
targetCompatibility = 1.8
System.out.println("group:"+group);
System.out.println("name:"+name);
System.out.println("version:"+version);
@@ -22,17 +25,34 @@ repositories {
mavenLocal()
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
publishing {
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 {
implementation "org.eclipse.jetty:jetty-server:11.0.1"
implementation "org.slf4j:slf4j-simple:2.0.0-alpha0"
//implementation 'com.hubspot.jinjava:jinjava:2.5.10'
implementation 'com.github.jknack:handlebars:4.2.1'
implementation 'com.h2database:h2:1.4.200'
implementation 'com.github.jknack:handlebars:4.3.0'
implementation 'com.h2database:h2:2.1.214'
// 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
implementation 'com.zaxxer:HikariCP:5.0.0'
@@ -180,13 +200,6 @@ task runServer{
//args "arg1", "arg2"
*/
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
}
eclipse{
classpath {
defaultOutputDir = file("build") ///default
+43 -131
View File
@@ -1,145 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<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"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- 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>
<groupId>com.reliancy.jabba</groupId>
<groupId>com.reliancy</groupId>
<artifactId>jabba</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>jabba</name>
<url>http://www.reliancy.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<version>0.1</version>
<licenses>
<license>
<name>The Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>11.0.7</version>
<version>12.0.0.alpha1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>11.0.1</version>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.0-alpha0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.hubspot.jinjava</groupId>
<artifactId>jinjava</artifactId>
<version>2.5.10</version>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars</artifactId>
<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>
</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>
@@ -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;
import java.io.IOException;
+167
View File
@@ -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);
}
}
+22
View File
@@ -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;
import java.util.Iterator;
@@ -9,78 +16,93 @@ public class Check implements Iterable<Check> {
public static abstract class Op{
public abstract boolean met(Check c,Object val);
}
/** logical AND operation. */
public static Op AND=new Op(){
public String toString(){return "AND";}
public boolean met(Check c,Object val){
return true;
}
};
/** logical OR operation. */
public static Op OR=new Op(){
public String toString(){return "OR";}
public boolean met(Check c,Object val){
return true;
}
};
/** logical NOT operation. */
public static Op NOT=new Op(){
public String toString(){return "NOT";}
public boolean met(Check c,Object val){
return true;
}
};
/** arithmetic equal test. */
public static Op EQ=new Op(){
public String toString(){return "=";}
public boolean met(Check c,Object val){
return true;
}
};
/** arithmetic negated equal test. */
public static Op NEQ=new Op(){
public String toString(){return "<>";}
public boolean met(Check c,Object val){
return true;
}
};
/** greater than check. */
public static Op GT=new Op(){
public String toString(){return ">";}
public boolean met(Check c,Object val){
return true;
}
};
/** greater than or equal check. */
public static Op GTE=new Op(){
public String toString(){return ">=";}
public boolean met(Check c,Object val){
return true;
}
};
/** less than check. */
public static Op LT=new Op(){
public String toString(){return "<";}
public boolean met(Check c,Object val){
return true;
}
};
/** less than or equal check. */
public static Op LTE=new Op(){
public String toString(){return "<=";}
public boolean met(Check c,Object val){
return true;
}
};
/** like check case insensitive. */
public static Op LIKE=new Op(){
public String toString(){return "LIKE";}
public boolean met(Check c,Object val){
return true;
}
};
/** set membership check. */
public static Op IN=new Op(){
public String toString(){return "IN";}
public boolean met(Check c,Object val){
return true;
}
};
/** negated set membership check. */
public static Op NOT_IN=new Op(){
public String toString(){return "NOT IN";}
public boolean met(Check c,Object val){
return true;
}
};
/** iterator over checks.
*
*/
public static class CheckIterator implements Iterator<Check>{
final Check root;
Check cur;
+7
View File
@@ -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;
import java.io.IOException;
+10 -2
View File
@@ -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;
import java.util.HashMap;
@@ -53,15 +60,16 @@ public class Entity extends Hdr{
* @param cls
* @return
*/
@SuppressWarnings("unchecked")
public static final Entity publish(Class<? extends DBO> cls){
Entity ret=registry.get(cls.getSimpleName());
if(ret!=null) return ret;
//System.out.println("Analyzing:"+cls);
Class base=cls.getSuperclass();
Class<?> base=cls.getSuperclass();
Entity base_ent=null;
int position0=0;
if(base!=null && base!=DBO.class){
base_ent=publish(base);
base_ent=publish((Class<? extends DBO>)base);
position0=base_ent.count();
}
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;
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;
import java.util.Iterator;
import java.util.List;
+9 -2
View File
@@ -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;
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.
* 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.
* @param filter
* @param params
* @param filter set of checks
* @param rec record to check
*/
public final void check_import(Check filter,DBO rec) {
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;
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;
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.
*
* TODO: no support for orderby yet
*/
public class SQLReader implements SiphonIterator<DBO>{
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;
import java.io.IOException;
+16 -2
View File
@@ -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;
import java.io.Closeable;
@@ -172,7 +179,14 @@ public class SQLWriter implements Closeable{
if(rec.getStatus()==DBO.Status.USED){
stmt=updateStmt;
// 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;
// copy values
@@ -199,7 +213,7 @@ public class SQLWriter implements Closeable{
}
}
}
}
}else
if(rec.getStatus()==DBO.Status.USED){
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;
import java.io.Closeable;
+8 -1
View File
@@ -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;
import java.io.IOException;
@@ -29,7 +36,7 @@ public interface Terminal {
Entity ent=Entity.recall(cls);
String sig="/"+ent.getName()+"/load";
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 {
+126
View File
@@ -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;
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 static interface Factory{
AppSession create(String id,App app);
}
final String id;
final HashMap<String,Object> values;
final App app;
final HashMap<String,Object> values=new HashMap<>();
long timeCreated;
long lastActive;
long maxAge;
SecurityActor user;
Feedback feedback;
public AppSession(String id){
public AppSession(String id,App app){
this.id=id;
values=new HashMap<>();
this.app=app;
lastActive=timeCreated=System.currentTimeMillis();
maxAge=1000*60*15;
}
@@ -27,6 +40,9 @@ public class AppSession implements Session{
public Object getValue(String key) {
return values.get(key);
}
public App getApp(){
return app;
}
public long getTimeInactive(){
return System.currentTimeMillis()-lastActive;
}
@@ -64,4 +80,12 @@ public class AppSession implements Session{
public void setUser(SecurityActor 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;
import java.io.IOException;
@@ -9,8 +16,16 @@ import java.util.UUID;
*/
public class AppSessionFilter extends Processor{
public static final String KEY_NAME="jbssid";
public AppSessionFilter() {
super(AppSessionFilter.class.getSimpleName().toLowerCase());
AppSession.Factory factory;
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
public void before(Request request, Response response) throws IOException {
@@ -23,7 +38,7 @@ public class AppSessionFilter extends Processor{
if(ss!=null){
if(ss.isExpired()){
// this app sessin expired - create a new one
ss=new AppSession(ssid);
ss=factory.create(ssid,app);
AppSession.setInstance(ssid, ss);
}else{
// this session is good
@@ -31,7 +46,7 @@ public class AppSessionFilter extends Processor{
}
}else{
// no session available
ss=new AppSession(ssid);
ss=factory.create(ssid,app);
AppSession.setInstance(ssid, ss);
}
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;
import java.util.ArrayList;
@@ -71,11 +78,11 @@ public class CallSession implements Session{
int len=callers.size();
return len>0?callers.get(len-1):null;
}
public static ThreadLocal<CallSession> instance=new ThreadLocal<>();
/**
* 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(){
CallSession ret=instance.get();
if(ret==null) instance.set(ret=new CallSession());
+33 -2
View File
@@ -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;
import org.slf4j.Logger;
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 save();
public String getId();
public Object getProperty(String key,Object def);
public Config setProperty(String key,Object val);
public <T> Config setProperty(Property<T> key,T 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;
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;
import com.reliancy.util.Resources;
import java.io.File;
@@ -40,7 +47,7 @@ public class FileServer extends EndPoint implements Resources.PathRewrite{
@Override
public void serve(Request request, Response response) throws IOException {
String path=request.getPath();
log().info("serving:"+path);
log().debug("to serve:"+path);
for(String prefix:map.keySet()){
boolean match=path.startsWith(prefix);
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
URL f=Resources.findFirst(this, rpath, sp);
if(f==null) continue; // skip if rpath not located
System.out.println("RES:"+f);
this.log().debug("\tfound:"+f);
writeResource(f,response);
return;
}
}
response.setStatus(Response.HTTP_NOT_FOUND);
response.getEncoder().writeln("missing file:{0}",path);
this.log().error("not found:"+path);
}
/**
* 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(){
return map.keySet().iterator();
}
public void exportRoutes(RouterEndPoint rep) {
public void exportRoutes(RoutedEndPoint rep) {
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;
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;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import com.reliancy.util.Handy;
@@ -12,16 +21,17 @@ public class MethodEndPoint extends EndPoint{
FULL, // one or more arguments need to do casting
}
Route route;
Routed route;
Object target;
Method method;
Parameter[] params;
Class<?> retType;
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());
this.route=r;
this.route=m.getAnnotation(Routed.class);
this.target=target;
this.method=m;
this.params=m.getParameters();
@@ -33,8 +43,27 @@ public class MethodEndPoint extends EndPoint{
if(params.length==0){
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
public void serve(Request request, Response response) throws IOException{
log().info("Serving method....{}",invokeType);
@@ -50,8 +79,7 @@ public class MethodEndPoint extends EndPoint{
encodeResponse(ret,response);
break;
}
default:{
// here we do full unmarshalling, marshalling
default:{ // here we do full unmarshalling, marshalling
Object[] argVals=decodeRequest(request);
ret=method.invoke(target,argVals);
encodeResponse(ret,response);
@@ -62,12 +90,6 @@ public class MethodEndPoint extends EndPoint{
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){
Object[] argVals=new Object[params.length];
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;
/** 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;
import java.io.IOException;
import org.slf4j.Logger;
@@ -7,7 +14,7 @@ public abstract class Processor {
protected Processor next;
protected String id;
protected boolean active;
protected Config config;
protected transient Config config;
protected Logger logger;
public Processor(String id){
@@ -63,15 +70,17 @@ public abstract class Processor {
ss.leave(this);
}
}
public void begin(Config conf){
public void begin(Config conf) throws Exception{
this.config=conf;
};
public void end(){
}
public void end() throws Exception{
this.config=null;
};
}
public void work() throws Exception{
}
protected Logger log(){
// 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(ret==null) ret=logger=LoggerFactory.getLogger(this.getId());
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;
import java.util.HashMap;
@@ -51,7 +58,8 @@ public class Request {
return http_request.getHeader(key);
}
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();
}
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;
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_UNAUTHORIZED=HttpServletResponse.SC_UNAUTHORIZED;
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 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;
import java.io.ByteArrayOutputStream;
@@ -67,7 +74,7 @@ public class ResponseEncoder {
getOutputStream().write(buf,offset, len);
return this;
}
public ResponseEncoder writeString(String str) throws IOException{
public ResponseEncoder writeString(CharSequence str) throws IOException{
getWriter().append(str);
return this;
}
@@ -79,11 +86,11 @@ public class ResponseEncoder {
}
return this;
}
public ResponseEncoder writeln(String msg,Object ... args) throws IOException{
public ResponseEncoder writeln(CharSequence msg,Object ... args) throws IOException{
if(args.length==0){
getWriter().append(msg).append("\n");
}else{
String str=MessageFormat.format(msg,args);
String str=MessageFormat.format(msg.toString(),args);
getWriter().append(str).append("\n");
}
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 "";
}
@@ -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;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.reliancy.util.Handy;
public class RouterEndPoint extends EndPoint{
HashMap<String,EndPoint> routes=new HashMap<>();
HashMap<String,ArrayList<String>> routeParams=new HashMap<>();
ArrayList<String> patterns=new ArrayList<>(); // route patterns ordered
public class RoutedEndPoint extends EndPoint{
HashMap<String,EndPoint> routes=new HashMap<>(); // route pattern to endpoint
ArrayList<RouteDetector> detectors=new ArrayList<>(); // route patterns ordered
int[] indexes; // indexes for each route within regex
Pattern regex;
public RouterEndPoint() {
super(null);
public RoutedEndPoint() {
super("router");
}
@Override
@@ -32,7 +39,6 @@ public class RouterEndPoint extends EndPoint{
if(m!=null){
//HashMap<String,String> pms=new HashMap<>();
String rt=evalMatcher(m,req.getPathParams());
//System.out.println(rt);
//System.out.println(req.getPathParams());
EndPoint ep=getRoute(rt);
if(ep!=null){
@@ -54,40 +60,30 @@ public class RouterEndPoint extends EndPoint{
return routes.get(r);
}
public void addRoute(String verb,String path, EndPoint mm) {
if(verb==null) verb="GET|POST|DELETE";
String pathPat=path.replaceAll("\\{(.+)\\}","(.+)");
String routePat=Handy.wrap(verb,"(",")")+" "+pathPat;
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);
RouteDetector det=new RouteDetector(verb,path);
detectors.add(det);
routes.put(det.getPattern(),mm);
}
public void compile() {
patterns.clear();
for(String r:routes.keySet()){
patterns.add(r);
}
// sort with longest first
Collections.sort(patterns,Comparator.comparing((str)->{return -str.length();}));
String fullPat = "("+String.join(")|(",patterns)+")";
Collections.sort(detectors,Comparator.comparing((det)->{return -det.getPath().length();}));
String fullPat=detectors
.stream()
.map(RouteDetector::toString)
.collect(Collectors.joining(")|("));
fullPat = "("+fullPat+")";
//System.out.println("FUll:"+fullPat);
regex=Pattern.compile(fullPat);
// also recompute indexes
indexes=new int[patterns.size()];
indexes=new int[detectors.size()];
int index=1;
for (int i = 0; i < indexes.length; i++) {
indexes[i]=index;
String p=patterns.get(i);
RouteDetector det=detectors.get(i);
index+=2; // this includes the verb group
if(routeParams.containsKey(p)){ // this includes any param groups
index+=routeParams.get(p).size();
if(det.hasParams()){ // this includes any param groups
index+=det.getParams().size();
}
}
//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.
* url params are saved in two ways by name and by pos.
* @param m
* @param routeParams
* @param m matcher to check
* @param p parameters to reference
* @return
*/
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(rindex<0) return null; // we can't match route to group
String ret=patterns.get(rindex);
if(p!=null && routeParams.containsKey(ret)){
ArrayList<String> pms=routeParams.get(ret);
RouteDetector det=detectors.get(rindex);
//String ret=patterns.get(rindex);
if(p!=null && det.hasParams()){
ArrayList<String> pms=det.getParams();
for(int i=0;i<pms.size();i++){
String val=m.group(gindex+2+i);
String byName=pms.get(i).toLowerCase();
@@ -130,6 +127,31 @@ public class RouterEndPoint extends EndPoint{
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;
/** 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;
};
}
@@ -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.
* 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.
*/
public interface SecurityPermit {
public SecurityStore getStore();
public Integer getId();
public SecurityActor getActor();
public Securable getSubject();
public boolean canRead();
@@ -13,4 +22,5 @@ public interface SecurityPermit {
public boolean canDelete();
public boolean canCreate();
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;
}
}
@@ -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.IOException;
import java.io.Writer;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
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.TemplateSource;
import com.github.jknack.handlebars.io.URLTemplateSource;
import com.reliancy.util.Resources;
import com.reliancy.util.ResultCode;
/*
import com.hubspot.jinjava.Jinjava;
@@ -49,17 +60,24 @@ public class Template {
public TemplateSource sourceAt(String location) throws IOException {
String fullpath=this.resolve(location);
URL loc=Resources.findFirst(null,fullpath,Template.search_path);
System.out.println(location+":"+loc+":"+fullpath);
//System.out.println(location+":"+loc+":"+fullpath);
if (loc == null) {
Logger.getLogger(Template.class.getSimpleName()).warning("Missing template"+fullpath);
throw new FileNotFoundException(location);
}
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 HashMap<String,Template> cache=new HashMap<>();
@@ -94,6 +112,7 @@ public class Template {
if(sp!=null && sp.length>0) search_path=sp;
return search_path;
}
public static final int ERR_BADTEMPLATE=ResultCode.defineFailure(0x01,Template.class,"bad template: ${template}");
com.github.jknack.handlebars.Template recipe;
final URL location;
String source;
@@ -114,13 +133,20 @@ public class Template {
if(source==null) this.source=Resources.toString(location);
return this;
}
public CharSequence render(Map<String,?> context) throws IOException{
public CharSequence render(Object context) throws IOException{
if(source==null) load();
//String ret = jinjava.render(source, context);
if(recipe==null){
recipe=handlebars.compileInline(source);
}
String ret=recipe.apply(context);
return ret;
return recipe.apply(context);
}
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;
/** Similar to a SAX interface used by parsers for XML and JSON to assemble DOM structures.
+7
View File
@@ -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;
import java.util.ArrayList;
+7
View File
@@ -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;
import java.io.IOException;
@@ -1,7 +1,9 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
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;
@@ -12,7 +14,7 @@ import com.reliancy.util.Tokenizer;
import com.reliancy.util.Handy;
/** 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
*/
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;
import java.io.IOException;
+7
View File
@@ -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;
import java.util.ArrayList;
+7
View File
@@ -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;
/**
+7
View File
@@ -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;
/**
@@ -1,7 +1,9 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
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;
+7
View File
@@ -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;
/**
* 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;
}
}
+46 -4
View File
@@ -1,7 +1,11 @@
package com.reliancy.util;
/**
* Common utility methods.
/*
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.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -19,18 +23,56 @@ import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
/**
* Common utility methods.
*/
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) {
if(verb==null) verb="";
if(verb.startsWith(left) && verb.endsWith(right)) return verb;
return left+verb.trim()+right;
}
/** remove left-right around verb.*/
public static String unwrap(String verb, String left, String right) {
if(verb==null) return verb;
String ret=verb.trim();
if(verb.startsWith(left) && verb.endsWith(right)) ret=verb.substring(1,verb.length()-1);
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){
return val!=null?val:def;
}
@@ -219,7 +261,7 @@ public final class Handy {
return bufs.toString();
}
/** 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
*/
public static String toCamelCase(String value) {
+8 -4
View File
@@ -1,7 +1,11 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
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.io.File;
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
* possibly takes care of looking up addresses.
* 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.
*
* 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.
* 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.
* @param paths paths joined with colon or semi-colon
* @param _paths paths joined with colon or semi-colon
* @return array of 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;
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.Iterator;
+18
View File
@@ -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}}
+23 -3
View File
@@ -1,4 +1,24 @@
{{#partial "content"}}
Calling base
{{#partial "MainSection"}}
<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">&copy; 2017-2018</p>
</form>
</div>
{{/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;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Date;
import com.reliancy.rec.JSON;
@@ -93,25 +99,38 @@ public class TerminalTest {
@Test
public void complexCRUD() throws IOException, SQLException{
System.out.println("ComplexCRUD");
// Reading
try(Action act=t.begin().load(Product.class).execute()){
for(DBO o:act){
System.out.println("DBO:"+o);
}
}
//Saving
Product p=new Product();
p.setStatus(DBO.Status.USED);
Product.id.set(p,35);
Product.kind.set(p,Product.class.getSimpleName());
Product.name.set(p,"myproduct");
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");
System.out.println("P0:"+JSON.toString(p));
System.out.println("Update P0:"+JSON.toString(p));
t.save(p);
System.out.println("P1:"+JSON.toString(p));
Product pp=t.load(Product.class, 35);
System.out.println("Update P1:"+JSON.toString(p));
// 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);
//t.delete(pp);
Entity.retract(Maps.class);
// Deleting
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;
import static org.junit.Assert.assertTrue;
@@ -31,8 +38,9 @@ public class RouterTest
{
//assertTrue( true );
System.out.println("Test router init...");
Router r=new Router();
RouterEndPoint rep=r.importEndPoints(r);
JettyApp r=new JettyApp();
RoutedEndPoint rep=new RoutedEndPoint();
rep.importMethods(r);
rep.compile();
//Matcher m=rep.match("GET","/helloPlain");
Matcher m=rep.match("GET","/hello3/45");
+8 -5
View File
@@ -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.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.Test;
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB