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,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;
public AppSession(String id){
Feedback feedback;
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;
public MethodEndPoint(Object target,Method m,Route r) {
ArrayList<MethodDecorator> decorators=new ArrayList<>();
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 = new Handlebars(new HBLoader());
static Handlebars handlebars;
static{
handlebars= new Handlebars(new HBLoader());
StringHelpers.register(handlebars);
/*
for(ConditionalHelpers h:ConditionalHelpers.values()){
}
*/
}
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,8 +1,10 @@
/*
* 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,8 +1,10 @@
/*
* 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;
}
}
+47 -5
View File
@@ -1,7 +1,11 @@
/*
Copyright (c) 2011-2022 Reliancy LLC
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
You may not use this file except in compliance with the License.
*/
package com.reliancy.util;
/**
* Common utility methods.
*/
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 String wrap(String verb, String left, String right) {
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) {
+10 -6
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;