fixes and support for h2c

This commit is contained in:
Amer Agovic
2024-01-21 17:02:31 -06:00
parent 89d58b33ce
commit feb5d4c163
16 changed files with 309 additions and 194 deletions
+16 -21
View File
@@ -17,15 +17,24 @@ application{
mainClass=(group+'.'+name+'.JettyApp')
}
java{
sourceCompatibility = 1.8
targetCompatibility = 1.8
// make our library a bit more compatible (jetty forced 11 else it would have been 1.8)
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
// print some info for orientation
//println("group:"+group);
//println("name:"+name);
//println("version:"+version);
//println("entry:"+mainClassName);
dependencies {
def jettyVersion="11.0.18"
implementation "org.eclipse.jetty:jetty-server:${jettyVersion}"
implementation "org.eclipse.jetty.http2:http2-server:${jettyVersion}"
implementation "org.slf4j:slf4j-jdk14:2.0.10"
//implementation "org.slf4j:slf4j-simple:2.0.10"
//implementation 'com.hubspot.jinjava:jinjava:2.5.10'
implementation 'com.github.jknack:handlebars:4.3.0'
implementation 'com.h2database:h2:2.1.214'
implementation 'org.postgresql:postgresql:42.5.0'
implementation 'com.zaxxer:HikariCP:5.0.0'
testImplementation "junit:junit:4.12"
}
sourceSets {
main {
@@ -97,20 +106,6 @@ javadoc {
links "https://docs.oracle.com/javase/8/docs/api/"
}
}
dependencies {
implementation "org.eclipse.jetty:jetty-server:11.0.1"
implementation "org.slf4j:slf4j-jdk14:2.0.10"
//implementation "org.slf4j:slf4j-simple:2.0.10"
//implementation 'com.hubspot.jinjava:jinjava:2.5.10'
implementation 'com.github.jknack:handlebars:4.3.0'
implementation 'com.h2database:h2:2.1.214'
// https://mvnrepository.com/artifact/org.postgresql/postgresql
implementation 'org.postgresql:postgresql:42.5.0'
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
implementation 'com.zaxxer:HikariCP:5.0.0'
testImplementation "junit:junit:4.12"
}
test {
environment "DB_URL", project.db_url
testLogging {
+105 -1
View File
@@ -9,8 +9,13 @@ You may not use this file except in compliance with the License.
package com.reliancy.jabba;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.reliancy.dbo.Terminal;
import com.reliancy.jabba.sec.SecurityPolicy;
import com.reliancy.jabba.ui.Rendering;
import com.reliancy.jabba.ui.Template;
import com.reliancy.util.CodeException;
import com.reliancy.util.ResultCode;
@@ -40,7 +45,8 @@ public abstract class App extends Processor{
protected Router router=null;
protected SecurityPolicy policy=null;
protected Terminal storage=null;
protected Map<String,AppModule> modules;
public App(String id) {
super(id);
}
@@ -57,6 +63,71 @@ public abstract class App extends Processor{
if(first!=null && resp.getStatus()==null) first.process(req, resp);
if(router!=null && resp.getStatus()==null) router.process(req,resp);
}
/** When an error occurs we need properly render exception.
* if html is accepted we try to render a valid response with n error within a template so it fits with the app.
* for all others we set error status code.
* for json,xml and plain we render into a message template for the rest we do nothing.
* this method returns true if a response was generated. in overloaded methods
* if false is returned we can generate response the status is set to 500 already.
* @param req incoming request
* @param ex exception state
* @param resp response to generate
* @return true if handled else it signifies we should do somthing in overloads.
* @throws IOException
*/
public boolean processError(com.reliancy.jabba.Request req,Throwable ex,com.reliancy.jabba.Response resp) throws IOException{
log().error("error:",ex);
String accepted_format=req.getHeader("Accept");
boolean present=accepted_format!=null;
if(present && (
accepted_format.contains("/html")
|| accepted_format.contains("/xhtml")
)){
// we have html request
resp.setContentType(HTTP.MIME_HTML);
Template t=Template.find("/templates/error.hbs");
if(t==null){ // no template found
resp.setStatus(Response.HTTP_INTERNAL_ERROR);
if(ex instanceof IOException) throw ((IOException)ex);
else throw new RuntimeException(ex);
}
Rendering.begin(t).with(ex).end(resp);
return true;
}else{
// for all other cases we first flag it as error
resp.setStatus(Response.HTTP_INTERNAL_ERROR);
}
// next we format a few common and supported messages
if(present && accepted_format.contains("/json")){
ResponseEncoder enc=resp.getEncoder();
if(enc.getErrorFormat()==null){
String template="'{'\n\t\"status\":\"error\",\n\t\"title\":\"{0}\",\n\t\"message\":\"{1}\"\n'}'\n";
enc.setErrorFormat(template);
}
enc.writeError(ex);
return true;
}
if(present && accepted_format.contains("/xml")){
ResponseEncoder enc=resp.getEncoder();
if(enc.getErrorFormat()==null){
String template="<response>\n\t<status>error</status>\n\t<title>{0}</title>\n\t<message>{1}</message>\n</response>\n";
enc.setErrorFormat(template);
}
enc.writeError(ex);
return true;
}
if(present && accepted_format.contains("text/plain")){
ResponseEncoder enc=resp.getEncoder();
if(enc.getErrorFormat()==null){
String template="status=error\n\ntitle={0}\n\nmessage={1}\n\n";
enc.setErrorFormat(template);
}
enc.writeError(ex);
return true;
}
return false;
}
/** add one or a chain of processors. */
public <T extends Processor> T addMiddleWare(T m){
if(m==null) return null;
@@ -183,4 +254,37 @@ public abstract class App extends Processor{
public Terminal getStorage(){
return storage;
}
/** register module under one or more names.
* if no names are provided just calls module to publish itself.
* @param module
* @param names
*/
public void publishModule(AppModule module,String...names){
if(names.length==0){
module.publish(this);
}else{
if(modules==null) modules=new HashMap<>();
for(String nm:names) if(nm!=null) modules.put(nm.toLowerCase(),module);
}
}
/** retracts module under given names.
* if no names provided calls on module to retract itself.
*/
public void retractModule(AppModule module,String...names){
if(names.length==0){
module.retract(this);
// if injected from outside we remove those too
if(modules!=null) while(modules.values().remove(module));
}else{
if(modules==null) return;
for(String nm:names){
if("*".equals(nm)){ // special case to remove all
while(modules.values().remove(module));
break;
}
modules.remove(nm.toLowerCase());
}
}
}
}
@@ -5,5 +5,7 @@ package com.reliancy.jabba;
*/
public interface AppModule {
void publish(App app);
default void retract(App app){};
default void retract(App app){
app.retractModule(this,"*");
};
}
@@ -130,6 +130,9 @@ public class ArgsConfig extends Config.Base{
APP_SETTINGS.set(this, cwd);
}
// also logging level and format
// System.out.println("LogLog:"+LOG_LEVEL.get(this));
// System.out.println("ENV:"+System.getenv("LOG_LEVEL"));
// LOG_LEVEL.set(this,"INFO");
Logger root=Log.setup();
Log.setLevel(root,LOG_LEVEL.get(this));
return this;
@@ -1,118 +0,0 @@
package com.reliancy.jabba;
import java.io.IOException;
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.ui.Feedback;
import com.reliancy.jabba.ui.FeedbackLine;
import com.reliancy.jabba.ui.Rendering;
import com.reliancy.jabba.ui.Template;
public class DemoEP implements AppModule{
@Override
public void publish(App app) {
app.getRouter().importMethods(this);
}
@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) {
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
AppSession ass=AppSession.getInstance();
try{
System.out.println("Post login");
String userid=(String)req.getParam("userid",null);
String pwd=(String)req.getParam("password",null);
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){
ass.getApp().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);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
@@ -140,11 +140,11 @@ public class FileServer extends EndPoint implements AppModule,Resources.PathRewr
String path=request.getPath();
Logger logger=log();
boolean atDebug=logger.isDebugEnabled();
if(atDebug) logger.debug("{0}:{1}",verb,path);
for(Bucket bucket:buckets){
String local_path=bucket.asContained(path);
if(local_path==null) continue; // this bucket is not accepting
if(HTTP.VERB_GET.equals(verb)){
if(atDebug) logger.debug("{}:{}",verb,path);
if(HTTP.VERB_GET.equals(verb)){
for(Bucket bucket:buckets){
String local_path=bucket.asContained(path);
if(local_path==null) continue; // this bucket is not accepting
try(InputStream ins=bucket.openSource(local_path,this)){
if(ins==null) continue; // url did not take
String etag=bucket.signature(local_path);
@@ -166,30 +166,38 @@ public class FileServer extends EndPoint implements AppModule,Resources.PathRewr
enc.writeStream(ins);
return; // we got something
}
}else{
// these verbs are not supported
}
}else{
// these verbs are not supported
}
response.setStatus(Response.HTTP_NOT_FOUND);
response.getEncoder().writeln("missing file:{0}",path);
logger.error("not found:{0}",path);
response.getEncoder().writeln("missing file:"+path);
logger.error("not found:{}",path);
}
/**
* Will render a URL resource to response.
* @param f
* @param response
*/
protected static void writeResource(URL f, Response response) throws IOException{
//log().info("writing:"+f);
ResponseEncoder enc=response.getEncoder();
public static boolean sendData(URL f, Response response) throws IOException{
try(InputStream is=f.openStream()){
String ctype=HTTP.guess_mime(f);
if(is==null) return false;
response.setStatus(Response.HTTP_OK);
String ctype=HTTP.guess_mime(f);
response.setContentType(ctype);
ResponseEncoder enc=response.getEncoder();
enc.writeStream(is);
return true;
}
}
/** adds a route which serves files.
public static boolean sendData(InputStream istr, Response response) throws IOException{
if(istr==null) return false;
ResponseEncoder enc=response.getEncoder();
response.setStatus(Response.HTTP_OK);
enc.writeStream(istr);
return true;
}
/** adds a route which serves files.
* if disk_path is ommited (0 len) or null we use Resources.search_path.
* @param bucket resource holder to add
*/
+29 -19
View File
@@ -8,6 +8,7 @@ You may not use this file except in compliance with the License.
package com.reliancy.jabba;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.EventListener;
import com.reliancy.jabba.sec.SecurityPolicy;
@@ -16,11 +17,19 @@ 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 com.reliancy.rec.JSONEncoder;
import com.reliancy.util.CodeException;
import com.reliancy.util.Log;
import com.reliancy.util.Resources;
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.MultiPartFormDataCompliance;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
@@ -28,10 +37,14 @@ 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.
* It will provide facilities to register endpoints via router.
* Mostly new routes are injected via AppModules which publish themselves.
* JettyApp installs ForwardCustomizer to react to reverse proxy setups.
*
*/
public class JettyApp extends App implements Handler{
enum State{
@@ -52,15 +65,22 @@ public class JettyApp extends App implements Handler{
jetty.setHandler(this);
_state=State.STOPPED;
}
/** implementation of jetty handler interface */
public Connector[] getConnectors(){
if(connectors!=null) return connectors;
ServerConnector connector = new ServerConnector(jetty);
connector.setReuseAddress(false);
connector.setPort(8090);
connectors=new Connector[] {connector};
// Create HTTP Config
HttpConfiguration httpConfig = new HttpConfiguration();
// Add support for X-Forwarded headers
httpConfig.addCustomizer( new ForwardedRequestCustomizer() );
// Create the http connector
HttpConnectionFactory http11 = new HttpConnectionFactory( httpConfig );
HTTP2ServerConnectionFactory h2c = new HTTP2CServerConnectionFactory(httpConfig);
ServerConnector httpConn = new ServerConnector(jetty,http11,h2c);
httpConn.setReuseAddress(false);
httpConn.setPort(8090);
connectors=new Connector[] {httpConn};
return connectors;
}
/** implementation of jetty handler interface */
@Override
public Server getServer() {
return jetty;
@@ -68,12 +88,10 @@ public class JettyApp extends App implements Handler{
@Override
public void setServer(Server arg0) {
System.out.println("setServer..."+jetty+"/"+arg0);
jetty=arg0;
}
@Override
public boolean addEventListener(EventListener arg0) {
System.out.println("adding evt listener...");
return false;
}
@Override
@@ -160,16 +178,8 @@ public class JettyApp extends App implements Handler{
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);
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);
log().error("error:",rex);
}catch(Exception ioex){
processError(req,ioex,resp);
}finally{
baseRequest.setHandled(true);
ss.end();
@@ -246,7 +256,7 @@ public class JettyApp extends App implements Handler{
app.setSecurityPolicy(secpol);
// install router
app.setRouter(new Router());
DemoEP ep=new DemoEP();
StatusMod ep=new StatusMod();
ep.publish(app);
// install file sever endpoint
FileServer fs=new FileServer("/static","/public");
@@ -24,7 +24,7 @@ public abstract class Processor {
public Processor(String id){
next=null;
this.id=id!=null?id:this.getClass().getSimpleName().toLowerCase();
this.id=id!=null?id:this.getClass().getSimpleName();
active=true;
}
public String getId(){
+37 -2
View File
@@ -18,6 +18,7 @@ import jakarta.servlet.http.HttpServletRequest;
public class Request {
final HttpServletRequest http_request;
final HashMap<String,String> pathParams=new HashMap<>();
String pathOverride;
public Request(HttpServletRequest http_request) {
this.http_request = http_request;
}
@@ -25,9 +26,16 @@ public class Request {
return pathParams;
}
public String getPath() {
return http_request.getPathInfo();
if(pathOverride!=null){
return pathOverride;
}else{
return http_request.getPathInfo();
}
}
public Request setPath(String path){
pathOverride=path;
return this;
}
public String getVerb() {
return http_request.getMethod();
}
@@ -76,6 +84,10 @@ public class Request {
"HTTP_FORWARDED",
"HTTP_VIA",
"REMOTE_ADDR" };
/**
* This method will consult several headers to obain ip address.
* @return best guess for remote address.
*/
public String getRemoteAddress() {
for (String header : HEADERS4IP) {
String ip = getHeader(header);
@@ -84,4 +96,27 @@ public class Request {
}
return http_request.getRemoteAddr();
}
/**
* will return shema://host:port/context
* @return everything preceeding the path.
*/
public String getMount(){
String scheme = http_request.getScheme();
String host = http_request.getHeader("Host"); // includes server name and server port
if(host==null || host.trim().isEmpty()){
// try differenty for host
String serverName = http_request.getServerName();
int serverPort = http_request.getServerPort();
host=serverName+":"+serverPort;
}
String resultPath = scheme + "://" + host;
String contextPath = http_request.getContextPath(); // includes leading forward slash
if(contextPath!=null){
resultPath+= contextPath;
}
return resultPath;
}
public String getProtocol(){
return http_request.getProtocol();
}
}
@@ -34,6 +34,7 @@ public class Response {
public static final int HTTP_TEMPORARY_REDIRECT=HttpServletResponse.SC_TEMPORARY_REDIRECT;
public static final int HTTP_FOUND_REDIRECT=HttpServletResponse.SC_FOUND;
public static final int HTTP_NOT_MODIFIED=HttpServletResponse.SC_NOT_MODIFIED;
public static final int HTTP_INTERNAL_ERROR=HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
final protected HttpServletResponse http_response;
final protected Writer char_response;
@@ -22,6 +22,9 @@ import java.text.MessageFormat;
import java.util.Collection;
import java.util.Iterator;
import com.reliancy.rec.JSONEncoder;
import com.reliancy.util.CodeException;
/**
* This class will replace the Java writer.
* It will have chainable calls. It will inherit lower level calls
@@ -35,6 +38,8 @@ public class ResponseEncoder implements Appendable,Closeable{
protected Writer writer;
protected OutputStream out;
protected Charset charSet;
protected String errorFmt;
public ResponseEncoder(Response r){
this(r,StandardCharsets.UTF_8);
//response=r;
@@ -118,6 +123,28 @@ public class ResponseEncoder implements Appendable,Closeable{
}
return this;
}
public ResponseEncoder setErrorFormat(String fmt){
errorFmt=fmt;
return this;
}
public String getErrorFormat(){
return this.errorFmt;
}
public ResponseEncoder writeError(Throwable ex) throws IOException{
if(errorFmt==null){
this.writeString(ex.toString());
}else{
StringBuilder title=new StringBuilder();
StringBuilder detail=new StringBuilder();
CodeException.fillUserMessage(ex, detail, title);
String body=MessageFormat.format(
errorFmt,
JSONEncoder.escape(title),
JSONEncoder.escape(detail));
writeString(body);
}
return this;
}
public ResponseEncoder writeObject(Object ret) throws IOException{
if(ret==null) return this;
Writer wr=getWriter();
+20 -1
View File
@@ -28,7 +28,7 @@ public class Router extends Processor{
Pattern regex;
public Router() {
super("router");
super("Router");
}
@Override
public void before(Request request, Response response) throws IOException {
@@ -64,9 +64,28 @@ public class Router extends Processor{
resp.getEncoder().writeln("could not resolve path:"+path);
}
}
/** Lookup of endpoints by full routing string.
* that includes verb.
* @param r routing path
* @return endpoint matching path
*/
public EndPoint getRoute(String r){
return routes.get(r);
}
/** Lookup of endpoint by method name or part of it.
* matches if endpoint id endswith method_name.
* matches case insensitively.
* @param method_name ending part of a method
* @return matched endpoint
*/
public EndPoint getRouteByMethod(String method_name){
method_name=method_name.toLowerCase();
for(EndPoint ep:routes.values()){
String nm=ep.getId().toLowerCase();
if(nm.endsWith(method_name)) return ep;
}
return null;
}
public void addRoute(String verb,String path, EndPoint mm) {
RouteDetector det=new RouteDetector(verb,path);
detectors.add(det);
@@ -0,0 +1,25 @@
package com.reliancy.jabba;
import java.io.IOException;
public class StatusMod implements AppModule{
@Override
public void publish(App app) {
app.publishModule(this,getClass().getSimpleName());
app.getRouter().importMethods(this);
}
@Routed(
path="/status"
)
public void status(Request req,Response resp) throws IOException{
StringBuilder buf=new StringBuilder();
if(buf!=null) throw new IOException("bummer!!!");
buf.append("Hi there!!!\n");
buf.append("mount:").append(req.getMount()).append("\n");
buf.append("path:").append(req.getPath()).append("\n");
buf.append("remote:").append(req.getRemoteAddress()).append("\n");
buf.append("protocol:").append(req.getProtocol()).append("\n");
resp.getEncoder().writeString(buf);
}
}
+11 -11
View File
@@ -140,7 +140,7 @@ public class JSONEncoder{
// we must be consistent so that repeated parse and encode works and not too smart here
// we need to put quotes around unless
if (!jsontxt) {
str = escape(str);
str = escape(str).toString();
if (o != null) {
o.append('"');
}
@@ -247,48 +247,48 @@ public class JSONEncoder{
* @param str input string
* @return output after encoding special chars
*/
public static String escape(String str) {
public static CharSequence escape(CharSequence str) {
StringBuilder buf = null;
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
switch (ch) {
case '"':
if(buf==null) buf=new StringBuilder(str.substring(0,i));
if(buf==null) buf=new StringBuilder(str.subSequence(0,i));
buf.append("\\\"");
break;
case '\\':
if(buf==null) buf=new StringBuilder(str.substring(0,i));
if(buf==null) buf=new StringBuilder(str.subSequence(0,i));
buf.append("\\\\");
break;
case '/':
if(buf==null) buf=new StringBuilder(str.substring(0,i));
if(buf==null) buf=new StringBuilder(str.subSequence(0,i));
buf.append("\\/");
break;
case '\b':
if(buf==null) buf=new StringBuilder(str.substring(0,i));
if(buf==null) buf=new StringBuilder(str.subSequence(0,i));
buf.append("\\b");
break;
case '\f':
if(buf==null) buf=new StringBuilder(str.substring(0,i));
if(buf==null) buf=new StringBuilder(str.subSequence(0,i));
buf.append("\\f");
break;
case '\n':
if(buf==null) buf=new StringBuilder(str.substring(0,i));
if(buf==null) buf=new StringBuilder(str.subSequence(0,i));
buf.append("\\n");
break;
case '\r':
if(buf==null) buf=new StringBuilder(str.substring(0,i));
if(buf==null) buf=new StringBuilder(str.subSequence(0,i));
buf.append("\\r");
break;
case '\t':
if(buf==null) buf=new StringBuilder(str.substring(0,i));
if(buf==null) buf=new StringBuilder(str.subSequence(0,i));
buf.append("\\t");
break;
default:
if(buf!=null) buf.append(ch);
}
}
return buf!=null?buf.toString():str;
return buf!=null?buf:str;
}
}
@@ -85,7 +85,7 @@ public class CodeException extends RuntimeException {
}
public static Throwable fillUserMessage(Throwable ex,StringBuilder msg,StringBuilder title) {
Throwable c = ex;
System.out.println(">>>"+c+"/"+c.getCause());
//System.out.println(">>>"+c+"/"+c.getCause());
while(c.getCause()!=null){
Throwable cc= c.getCause();
if(c.getMessage()==null){
@@ -93,19 +93,19 @@ public class CodeException extends RuntimeException {
}
String cMsg=c.getMessage();
String ccMsg=cc.getMessage();
System.out.println("!!!"+cMsg+"/"+c.getClass().getName()+"/"+cc.getClass().getName());
//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());
//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);
//System.out.println("CC:"+c);
// take care of title
String _title=c.getClass().getSimpleName();
if(c instanceof CodeException){
@@ -41,6 +41,7 @@ public class DemoApp extends JettyApp implements AppModule{
Resources.appendSearch(0,cls);
String work_dir=ArgsConfig.APP_WORKDIR.get(conf);
if(work_dir!=null) Resources.appendSearch(0,work_dir);
log().info("work_dir:{}",work_dir);
//for(Object p:Resources.search_path){
// System.out.println("sp:"+p);
//}
@@ -52,7 +53,7 @@ public class DemoApp extends JettyApp implements AppModule{
app.setSecurityPolicy(secpol);
// install router
app.setRouter(new Router());
DemoEP ep=new DemoEP();
StatusMod ep=new StatusMod();
ep.publish(app);
// install file sever endpoint
FileServer fs=new FileServer("/static","/public");
@@ -60,6 +61,8 @@ public class DemoApp extends JettyApp implements AppModule{
Menu top_menu=Menu.request(Menu.TOP);
top_menu.add(new MenuItem("home")).addSpacer().add(new MenuItem("login"));
top_menu.setTitle("Jabba3");
app.getRouter().compile();
System.out.println(app.getRouter().regex);
}
@Override
public void publish(App app) {
@@ -120,6 +123,7 @@ public class DemoApp extends JettyApp implements AppModule{
@Routed
public void login(com.reliancy.jabba.Request req,Response resp){
//return "login form here";
log().info("login here");
if(req.getVerb().equals("POST")){
// here we need to process login and redirect
AppSession ass=AppSession.getInstance();