diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..3bf4578
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.project b/.project
new file mode 100644
index 0000000..c4e3f9b
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+
+
+ achi-data
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/CheckStyle.xml b/CheckStyle.xml
new file mode 100755
index 0000000..d68aedd
--- /dev/null
+++ b/CheckStyle.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CleanUp.xml b/CleanUp.xml
new file mode 100644
index 0000000..9df98d2
--- /dev/null
+++ b/CleanUp.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Formatter.xml b/Formatter.xml
new file mode 100644
index 0000000..b775e22
--- /dev/null
+++ b/Formatter.xml
@@ -0,0 +1,366 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a9d1e81
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,6 @@
+PROPIETARY licence
+==================
+
+Copyright at Edouard DUPIN
+
+you have no right
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..54d39e2
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,177 @@
+
+ 4.0.0
+ kar
+ archidata
+ 0.1.0
+
+ 2.1
+ 2.32
+ 2.3.1
+ 3.0.7
+
+ 3.1
+ 17
+ 17
+
+ 3.1.1
+
+
+
+
+
+
+ org.glassfish.jersey
+ jersey-bom
+ ${jersey.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.glassfish.jersey.media
+ jersey-media-multipart
+
+
+ org.glassfish.jersey.inject
+ jersey-hk2
+
+
+ org.glassfish.jersey.containers
+ jersey-container-grizzly2-http
+
+
+ javax.xml.bind
+ jaxb-api
+ ${jaxb.version}
+
+
+ javax.ws.rs
+ javax.ws.rs-api
+ 2.1.1
+
+
+ com.sun.xml.bind
+ jaxb-impl
+ ${jaxb.version}
+
+
+ com.sun.istack
+ istack-commons-runtime
+ ${istack.version}
+
+
+ org.glassfish.jersey.test-framework.providers
+ jersey-test-framework-provider-grizzly2
+ test
+
+
+ mysql
+ mysql-connector-java
+ 8.0.30
+
+
+ org.glassfish.jersey.media
+ jersey-media-json-jackson
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.8.10
+
+
+ javax.servlet
+ javax.servlet-api
+ 3.0.1
+ compile
+
+
+ org.jetbrains
+ annotations
+ RELEASE
+ compile
+
+
+ com.nimbusds
+ nimbus-jose-jwt
+ 9.22
+
+
+
+
+ src
+ test/src
+ ${project.basedir}/out/maven/
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven.compiler.version}
+
+
+ ${maven.compiler.target}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M5
+
+
+ maven-assembly-plugin
+
+
+
+ fully.qualified.MainClass
+
+
+
+ jar-with-dependencies
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.2.0
+
+ private
+ true
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.2.0
+
+ public
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/org/kar/archidata/GlobalConfiguration.java b/src/org/kar/archidata/GlobalConfiguration.java
new file mode 100644
index 0000000..7e18866
--- /dev/null
+++ b/src/org/kar/archidata/GlobalConfiguration.java
@@ -0,0 +1,16 @@
+package org.kar.archidata;
+
+import org.kar.archidata.db.DBConfig;
+import org.kar.archidata.util.ConfigBaseVariable;
+
+public class GlobalConfiguration {
+ public static DBConfig dbConfig = null;;
+
+ static {
+ dbConfig = new DBConfig(ConfigBaseVariable.getDBHost(),
+ Integer.parseInt(ConfigBaseVariable.getDBPort()),
+ ConfigBaseVariable.getDBLogin(),
+ ConfigBaseVariable.getDBPassword(),
+ ConfigBaseVariable.getDBName());
+ }
+}
diff --git a/src/org/kar/archidata/SqlWrapper.java b/src/org/kar/archidata/SqlWrapper.java
new file mode 100644
index 0000000..5aafed9
--- /dev/null
+++ b/src/org/kar/archidata/SqlWrapper.java
@@ -0,0 +1,1205 @@
+package org.kar.archidata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.sql.*;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+import org.kar.archidata.annotation.SQLAutoIncrement;
+import org.kar.archidata.annotation.SQLComment;
+import org.kar.archidata.annotation.SQLIfNotExists;
+import org.kar.archidata.annotation.SQLLimitSize;
+import org.kar.archidata.annotation.SQLNotNull;
+import org.kar.archidata.annotation.SQLNotRead;
+import org.kar.archidata.annotation.SQLPrimaryKey;
+import org.kar.archidata.annotation.SQLTableLinkGeneric;
+import org.kar.archidata.annotation.SQLTableLinkGeneric.ModelLink;
+import org.kar.archidata.annotation.SQLTableName;
+import org.kar.archidata.annotation.SQLUpdateTime;
+import org.kar.archidata.db.DBEntry;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.kar.archidata.annotation.SQLCreateTime;
+import org.kar.archidata.annotation.SQLDefault;
+
+
+public class SqlWrapper {
+
+ public static class ExceptionDBInterface extends Exception {
+ public int errorID;
+ ExceptionDBInterface(int errorId, String message) {
+ super(message);
+ this.errorID = errorId;
+ }
+ }
+
+ public SqlWrapper() {
+
+ }
+
+ public static String convertTypeInSQL(Class> type) throws Exception {
+ if (type == Long.class || type == long.class ) {
+ return "bigint";
+ }
+ if (type == Integer.class || type == int.class ) {
+ return "int";
+ }
+ if (type == Boolean.class || type == boolean.class) {
+ return "tinyint(1)";
+ }
+ if (type == Float.class || type == float.class) {
+ return "float";
+ }
+ if (type == Double.class || type == double.class) {
+ return "double";
+ }
+ if (type == Timestamp.class) {
+ return "timestamp(3)";
+ }
+ if (type == Date.class) {
+ return "date";
+ }
+ if (type == String.class) {
+ return "text";
+ }
+ throw new Exception("Imcompatible type of element in object for: " + type.getCanonicalName());
+ }
+
+ protected static void setValuedb(Class> type, T data, int index, Field field, PreparedStatement ps) throws IllegalArgumentException, IllegalAccessException, SQLException {
+ if (type == Long.class) {
+ Object tmp = field.get(data);
+ if (tmp == null) {
+ ps.setNull(index++, Types.BIGINT);
+ } else {
+ ps.setLong(index++, (Long)tmp);
+ }
+ } else if (type == long.class ) {
+ ps.setLong(index++, field.getLong(data));
+ } else if (type == Integer.class) {
+ Object tmp = field.get(data);
+ if (tmp == null) {
+ ps.setNull(index++, Types.INTEGER);
+ } else {
+ ps.setInt(index++, (Integer)tmp);
+ }
+ } else if (type == int.class ) {
+ ps.setInt(index++, field.getInt(data));
+ } else if (type == Float.class) {
+ Object tmp = field.get(data);
+ if (tmp == null) {
+ ps.setNull(index++, Types.FLOAT);
+ } else {
+ ps.setFloat(index++, (Float)tmp);
+ }
+ } else if (type == float.class) {
+ ps.setFloat(index++, field.getFloat(data));
+ } else if (type == Double.class) {
+ Object tmp = field.get(data);
+ if (tmp == null) {
+ ps.setNull(index++, Types.DOUBLE);
+ } else {
+ ps.setDouble(index++, (Double)tmp);
+ }
+ } else if (type == Double.class) {
+ ps.setDouble(index++, field.getDouble(data));
+ } else if (type == Boolean.class) {
+ Object tmp = field.get(data);
+ if (tmp == null) {
+ ps.setNull(index++, Types.INTEGER);
+ } else {
+ ps.setBoolean(index++, (Boolean)tmp);
+ }
+ } else if (type == boolean.class) {
+ ps.setBoolean(index++, field.getBoolean(data));
+ } else if (type == Timestamp.class) {
+ Object tmp = field.get(data);
+ if (tmp == null) {
+ ps.setNull(index++, Types.INTEGER);
+ } else {
+ ps.setTimestamp(index++, (Timestamp)tmp);
+ }
+ } else if (type == Date.class) {
+ Object tmp = field.get(data);
+ if (tmp == null) {
+ ps.setNull(index++, Types.INTEGER);
+ } else {
+ ps.setDate(index++, (Date)tmp);
+ }
+ } else if (type == String.class) {
+ Object tmp = field.get(data);
+ if (tmp == null) {
+ ps.setNull(index++, Types.VARCHAR);
+ } else {
+ ps.setString(index++, (String)tmp);
+ }
+ }
+ }
+ protected static void setValueFromDb(Class> type, T data, int index, Field field, ResultSet rs) throws IllegalArgumentException, IllegalAccessException, SQLException {
+ if (type == Long.class) {
+ Long tmp = rs.getLong(index);
+ if (rs.wasNull()) {
+ field.set(data, null);
+ } else {
+ //System.out.println(" ==> " + tmp);
+ field.set(data, tmp);
+ }
+ } else if (type == long.class ) {
+ Long tmp = rs.getLong(index);
+ if (rs.wasNull()) {
+ //field.set(data, null);
+ } else {
+ field.setLong(data, tmp);
+ }
+ } else if (type == Integer.class) {
+ Integer tmp = rs.getInt(index);
+ if (rs.wasNull()) {
+ field.set(data, null);
+ } else {
+ field.set(data, tmp);
+ }
+ } else if (type == int.class ) {
+ Integer tmp = rs.getInt(index);
+ if (rs.wasNull()) {
+ //field.set(data, null);
+ } else {
+ field.setInt(data, tmp);
+ }
+ } else if (type == Float.class) {
+ Float tmp = rs.getFloat(index);
+ if (rs.wasNull()) {
+ field.set(data, null);
+ } else {
+ field.set(data, tmp);
+ }
+ } else if (type == float.class) {
+ Float tmp = rs.getFloat(index);
+ if (rs.wasNull()) {
+ //field.set(data, null);
+ } else {
+ field.setFloat(data, tmp);
+ }
+ } else if (type == Double.class) {
+ Double tmp = rs.getDouble(index);
+ if (rs.wasNull()) {
+ field.set(data, null);
+ } else {
+ field.set(data, tmp);
+ }
+ } else if (type == double.class) {
+ Double tmp = rs.getDouble(index);
+ if (rs.wasNull()) {
+ //field.set(data, null);
+ } else {
+ field.setDouble(data, tmp);
+ }
+ } else if (type == Boolean.class) {
+ Boolean tmp = rs.getBoolean(index);
+ if (rs.wasNull()) {
+ field.set(data, null);
+ } else {
+ field.set(data, tmp);
+ }
+ } else if (type == boolean.class) {
+ Boolean tmp = rs.getBoolean(index);
+ if (rs.wasNull()) {
+ //field.set(data, null);
+ } else {
+ field.setBoolean(data, tmp);
+ }
+ } else if (type == Timestamp.class) {
+ Timestamp tmp = rs.getTimestamp(index);
+ if (rs.wasNull()) {
+ field.set(data, null);
+ } else {
+ field.set(data, tmp);
+ }
+ } else if (type == Date.class) {
+ Date tmp = rs.getDate(index);
+ if (rs.wasNull()) {
+ field.set(data, null);
+ } else {
+ field.set(data, tmp);
+ }
+ } else if (type == String.class) {
+ String tmp = rs.getString(index);
+ if (rs.wasNull()) {
+ field.set(data, null);
+ } else {
+ field.set(data, tmp);
+ }
+ }
+ }
+ public static ModelLink getLinkMode(Field elem) {
+ SQLTableLinkGeneric[] decorators = elem.getDeclaredAnnotationsByType(SQLTableLinkGeneric.class);
+ if (decorators == null || decorators.length == 0) {
+ return SQLTableLinkGeneric.ModelLink.NONE;
+ }
+ return decorators[0].value();
+ }
+
+ public static T insert(T data) throws Exception {
+ Class> clazz = data.getClass();
+ //public static NodeSmall createNode(String typeInNode, String name, String descrition, Long parentId) {
+
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ // real add in the BDD:
+ try {
+ String tableName = getTableName(clazz);
+ boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
+ StringBuilder query = new StringBuilder();
+ query.append("INSERT INTO ");
+ query.append(tableName);
+ query.append(" (");
+
+ boolean firstField = true;
+ int count = 0;
+ for (Field elem : clazz.getFields()) {
+ boolean primaryKey = elem.getDeclaredAnnotationsByType(SQLPrimaryKey.class).length != 0;
+ if (primaryKey) {
+ continue;
+ }
+ ModelLink linkGeneric = getLinkMode(elem);
+ if (linkGeneric == ModelLink.EXTERNAL) {
+ continue;
+ }
+ boolean createTime = elem.getDeclaredAnnotationsByType(SQLCreateTime.class).length != 0;
+ if (createTime) {
+ continue;
+ }
+ boolean updateTime = elem.getDeclaredAnnotationsByType(SQLUpdateTime.class).length != 0;
+ if (updateTime) {
+ continue;
+ }
+ if (!elem.getClass().isPrimitive()) {
+ Object tmp = elem.get(data);
+ if(tmp == null && elem.getDeclaredAnnotationsByType(SQLDefault.class).length != 0) {
+ continue;
+ }
+ }
+ count++;
+ String name = elem.getName();
+ if (firstField) {
+ firstField = false;
+ } else {
+ query.append(",");
+ }
+ query.append(" `");
+ query.append(name);
+ query.append("`");
+ }
+ firstField = true;
+ query.append(") VALUES (");
+ for (int iii = 0; iii type = elem.getType();
+ if (!type.isPrimitive()) {
+ Object tmp = elem.get(data);
+ if(tmp == null && elem.getDeclaredAnnotationsByType(SQLDefault.class).length != 0) {
+ continue;
+ }
+ }
+ setValuedb(type, data, iii++, elem, ps);
+ } else {
+ // transform the data in string to insert it ...
+ Object tmp = elem.get(data);
+ if (tmp == null) {
+ ps.setNull(iii++, Types.BIGINT);
+ } else {
+ @SuppressWarnings("unchecked")
+ String dataTmp = getStringOfIds((List)tmp);
+ ps.setString(iii++, dataTmp);
+ }
+ }
+ count++;
+ }
+ // execute the request
+ int affectedRows = ps.executeUpdate();
+ if (affectedRows == 0) {
+ throw new SQLException("Creating node failed, no rows affected.");
+ }
+ Long uniqueSQLID = null;
+ // retreive uid inserted
+ try (ResultSet generatedKeys = ps.getGeneratedKeys()) {
+ if (generatedKeys.next()) {
+ uniqueSQLID = generatedKeys.getLong(1);
+ } else {
+ throw new SQLException("Creating node failed, no ID obtained (1).");
+ }
+ } catch (Exception ex) {
+ System.out.println("Can not get the UID key inserted ... ");
+ ex.printStackTrace();
+ throw new SQLException("Creating node failed, no ID obtained (2).");
+ }
+ if (primaryKeyField != null) {
+ if (primaryKeyField.getType() == Long.class) {
+ primaryKeyField.set(data, (Long)uniqueSQLID);
+ } else if (primaryKeyField.getType() == long.class) {
+ primaryKeyField.setLong(data, uniqueSQLID);
+ } else {
+ System.out.println("Can not manage the primary filed !!!");
+ }
+ }
+ //ps.execute();
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ return data;
+ }
+
+ public static T insertWithJson(Class clazz, String jsonData) throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ // parse the object to be sure the data are valid:
+ T data = mapper.readValue(jsonData, clazz);
+ return insert(data);
+ }
+
+ public static void update(Class clazz, long id, String jsonData) throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ // parse the object to be sure the data are valid:
+ T data = mapper.readValue(jsonData, clazz);
+ // Read the tree to filter injection of data:
+ JsonNode root = mapper.readTree(jsonData);
+ List keys = new ArrayList<>();
+ Iterator iterator = root.fieldNames();
+ iterator.forEachRemaining(e -> keys.add(e));
+ update(data, id, keys);
+ }
+
+ public static void update(T data, long id, List filterValue) throws Exception {
+ Class> clazz = data.getClass();
+ //public static NodeSmall createNode(String typeInNode, String name, String description, Long parentId) {
+
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ // real add in the BDD:
+ try {
+ String tableName = getTableName(clazz);
+ boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
+ StringBuilder query = new StringBuilder();
+ query.append("UPDATE ");
+ query.append(tableName);
+ query.append(" SET ");
+
+ boolean firstField = true;
+ Field primaryKeyField = null;
+ int count = 0;
+ for (Field elem : clazz.getFields()) {
+ boolean primaryKey = elem.getDeclaredAnnotationsByType(SQLPrimaryKey.class).length != 0;
+ if (primaryKey) {
+ primaryKeyField = elem;
+ continue;
+ }
+ ModelLink linkGeneric = getLinkMode(elem);
+ if (linkGeneric == ModelLink.EXTERNAL) {
+ continue;
+ }
+ boolean createTime = elem.getDeclaredAnnotationsByType(SQLCreateTime.class).length != 0;
+ if (createTime) {
+ continue;
+ }
+ String name = elem.getName();
+ boolean updateTime = elem.getDeclaredAnnotationsByType(SQLUpdateTime.class).length != 0;
+ if (! updateTime && !filterValue.contains(name)) {
+ continue;
+ }
+ if (!elem.getClass().isPrimitive()) {
+ Object tmp = elem.get(data);
+ if(tmp == null && elem.getDeclaredAnnotationsByType(SQLDefault.class).length != 0) {
+ continue;
+ }
+ }
+ count++;
+ if (firstField) {
+ firstField = false;
+ } else {
+ query.append(",");
+ }
+ query.append(" `");
+ query.append(name);
+ query.append("` = ");
+ if (updateTime) {
+ query.append(" now(3) ");
+ } else {
+ query.append("? ");
+ }
+ }
+ query.append(" WHERE `");
+ query.append(primaryKeyField.getName());
+ query.append("` = ?");
+ firstField = true;
+ System.out.println("generate the querry: '" + query.toString() + "'");
+ // prepare the request:
+ PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
+ int iii = 1;
+ for (Field elem : clazz.getFields()) {
+ boolean primaryKey = elem.getDeclaredAnnotationsByType(SQLPrimaryKey.class).length != 0;
+ if (primaryKey) {
+ continue;
+ }
+ ModelLink linkGeneric = getLinkMode(elem);
+ if (linkGeneric == ModelLink.EXTERNAL) {
+ continue;
+ }
+ boolean createTime = elem.getDeclaredAnnotationsByType(SQLCreateTime.class).length != 0;
+ if (createTime) {
+ continue;
+ }
+ String name = elem.getName();
+ boolean updateTime = elem.getDeclaredAnnotationsByType(SQLUpdateTime.class).length != 0;
+ if (updateTime || !filterValue.contains(name)) {
+ continue;
+ }
+ if (linkGeneric == ModelLink.NONE) {
+ Class> type = elem.getType();
+ if (!type.isPrimitive()) {
+ Object tmp = elem.get(data);
+ if(tmp == null && elem.getDeclaredAnnotationsByType(SQLDefault.class).length != 0) {
+ continue;
+ }
+ }
+ setValuedb(type, data, iii++, elem, ps);
+ } else {
+ // transform the data in string to insert it ...
+ Object tmp = elem.get(data);
+ if (tmp == null) {
+ ps.setNull(iii++, Types.BIGINT);
+ } else {
+ @SuppressWarnings("unchecked")
+ String dataTmp = getStringOfIds((List)tmp);
+ ps.setString(iii++, dataTmp);
+ }
+ }
+ count++;
+ }
+ ps.setLong(iii++, id);
+ // execute the request
+ int affectedRows = ps.executeUpdate();
+ // todo manage the list of element is valid ...
+ //ps.execute();
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ public static T getWith(Class clazz, String key, String value) throws Exception {
+ //public static NodeSmall createNode(String typeInNode, String name, String description, Long parentId) {
+
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ T out = null;
+ // real add in the BDD:
+ try {
+ String tableName = getTableName(clazz);
+ boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
+ StringBuilder query = new StringBuilder();
+ query.append("SELECT ");
+ //query.append(tableName);
+ //query.append(" SET ");
+
+ boolean firstField = true;
+ Field primaryKeyField = null;
+ int count = 0;
+ for (Field elem : clazz.getFields()) {
+ boolean primaryKey = elem.getDeclaredAnnotationsByType(SQLPrimaryKey.class).length != 0;
+ if (primaryKey) {
+ primaryKeyField = elem;
+ }
+ ModelLink linkGeneric = getLinkMode(elem);
+ if (linkGeneric != ModelLink.NONE) {
+ continue;
+ }
+ boolean createTime = elem.getDeclaredAnnotationsByType(SQLCreateTime.class).length != 0;
+ if (createTime) {
+ continue;
+ }
+ String name = elem.getName();
+ boolean updateTime = elem.getDeclaredAnnotationsByType(SQLUpdateTime.class).length != 0;
+ if (updateTime) {
+ continue;
+ }
+ count++;
+ if (firstField) {
+ firstField = false;
+ } else {
+ query.append(",");
+ }
+ query.append(" ");
+ query.append(tableName);
+ query.append(".");
+ query.append(name);
+ }
+ query.append("\n FROM `");
+ query.append(tableName);
+ query.append("` ");
+ query.append("\n WHERE ");
+ query.append(tableName);
+ query.append(".");
+ query.append(key);
+ query.append(" = ?");
+ query.append("\n LIMIT 1 ");
+ /*
+ query.append(" AND ");
+ query.append(tableName);
+ query.append(".deleted = false ");
+ */
+ firstField = true;
+ System.out.println("generate the querry: '" + query.toString() + "'");
+ // prepare the request:
+ PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
+ int iii = 1;
+ ps.setString(iii++, value);
+ // execute the request
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ Object data = clazz.getConstructors()[0].newInstance();
+ count = 1;
+ for (Field elem : clazz.getFields()) {
+ ModelLink linkGeneric = getLinkMode(elem);
+ if (linkGeneric != ModelLink.NONE) {
+ continue;
+ }
+ boolean createTime = elem.getDeclaredAnnotationsByType(SQLCreateTime.class).length != 0;
+ if (createTime) {
+ continue;
+ }
+ String name = elem.getName();
+ boolean updateTime = elem.getDeclaredAnnotationsByType(SQLUpdateTime.class).length != 0;
+ if (updateTime) {
+ continue;
+ }
+ //this.name = rs.getString(iii++);
+ //this.description = rs.getString(iii++);
+ //this.covers = getListOfIds(rs, iii++);
+ setValueFromDb(elem.getType(), data, count, elem, rs);
+ count++;
+ }
+ out = (T)data;
+ }
+
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ entry.disconnect();
+ entry = null;
+ return out;
+ }
+
+ public static T getWhere(Class clazz, String key, String operator, Object value ) throws Exception {
+
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ T out = null;
+ // real add in the BDD:
+ try {
+ String tableName = getTableName(clazz);
+ boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
+ StringBuilder query = new StringBuilder();
+ query.append("SELECT ");
+ //query.append(tableName);
+ //query.append(" SET ");
+
+ boolean firstField = true;
+ int count = 0;
+ for (Field elem : clazz.getFields()) {
+ ModelLink linkGeneric = getLinkMode(elem);
+ if (linkGeneric != ModelLink.NONE) {
+ continue;
+ }
+ boolean createTime = elem.getDeclaredAnnotationsByType(SQLCreateTime.class).length != 0;
+ if (createTime) {
+ continue;
+ }
+ String name = elem.getName();
+ boolean updateTime = elem.getDeclaredAnnotationsByType(SQLUpdateTime.class).length != 0;
+ if (updateTime) {
+ continue;
+ }
+ count++;
+ if (firstField) {
+ firstField = false;
+ } else {
+ query.append(",");
+ }
+ query.append(" ");
+ query.append(tableName);
+ query.append(".");
+
+ query.append(name);
+ }
+ query.append(" FROM `");
+ query.append(tableName);
+ query.append("` ");
+ query.append(" WHERE ");
+ query.append(tableName);
+ query.append(".");
+ query.append(key);
+ query.append(" ");
+ query.append(operator);
+ query.append(" ?");
+ /*
+ query.append(" AND ");
+ query.append(tableName);
+ query.append(".deleted = false ");
+ */
+ firstField = true;
+ System.out.println("generate the querry: '" + query.toString() + "'");
+ // prepare the request:
+ PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
+ int iii = 1;
+ if (value.getClass() == Long.class) {
+ ps.setLong(iii++, (Long)value);
+ } else if (value.getClass() == Integer.class) {
+ ps.setInt(iii++, (Integer)value);
+ } else if (value.getClass() == String.class) {
+ ps.setString(iii++, (String)value);
+ } else if (value.getClass() == Short.class) {
+ ps.setShort(iii++, (Short)value);
+ } else if (value.getClass() == Byte.class) {
+ ps.setByte(iii++, (Byte)value);
+ } else {
+ throw new Exception("Not manage type ==> need to add it ...");
+ }
+ // execute the request
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ Object data = clazz.getConstructors()[0].newInstance();
+ count = 1;
+ for (Field elem : clazz.getFields()) {
+ ModelLink linkGeneric = getLinkMode(elem);
+ if (linkGeneric != ModelLink.NONE) {
+ continue;
+ }
+ boolean createTime = elem.getDeclaredAnnotationsByType(SQLCreateTime.class).length != 0;
+ if (createTime) {
+ continue;
+ }
+ String name = elem.getName();
+ boolean updateTime = elem.getDeclaredAnnotationsByType(SQLUpdateTime.class).length != 0;
+ if (updateTime) {
+ continue;
+ }
+ //this.name = rs.getString(iii++);
+ //this.description = rs.getString(iii++);
+ //this.covers = getListOfIds(rs, iii++);
+ setValueFromDb(elem.getType(), data, count, elem, rs);
+ count++;
+ }
+ out = (T)data;
+ }
+
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ entry.disconnect();
+ entry = null;
+ return out;
+ }
+
+ public static T get(Class clazz, long id) throws Exception {
+ Field primaryKeyField = null;
+ for (Field elem : clazz.getFields()) {
+ boolean primaryKey = elem.getDeclaredAnnotationsByType(SQLPrimaryKey.class).length != 0;
+ if (primaryKey) {
+ primaryKeyField = elem;
+ }
+ }
+ if (primaryKeyField != null) {
+ return getWhere(clazz, primaryKeyField.getName(), "=", id);
+ }
+ throw new Exception("Missing primary Key...");
+ }
+
+ private enum StateLoad {
+ DISABLE,
+ NORMAL,
+ ARRAY
+ };
+
+ public static String getCurrentTimeStamp() {
+ return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
+ }
+
+ public static List gets(Class clazz, boolean fool) throws Exception {
+ System.out.println("request get " + clazz.getCanonicalName() + " start @" + getCurrentTimeStamp());
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ List out = new ArrayList<>();
+ // real add in the BDD:
+ try {
+ String tableName = getTableName(clazz);
+ boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
+ StringBuilder query = new StringBuilder();
+ query.append("SELECT ");
+ //query.append(tableName);
+ //query.append(" SET ");
+
+ boolean firstField = true;
+ int count = 0;
+ StateLoad[] autoClasify = new StateLoad[clazz.getFields().length];
+ int indexAutoClasify = 0;
+ for (Field elem : clazz.getFields()) {
+
+ boolean notRead = elem.getDeclaredAnnotationsByType(SQLNotRead.class).length != 0;
+ if (!fool && notRead) {
+ autoClasify[indexAutoClasify++] = StateLoad.DISABLE;
+ continue;
+ }
+ String name = elem.getName();
+ count++;
+ if (firstField) {
+ firstField = false;
+ } else {
+ query.append(",");
+ }
+ ModelLink linkGeneric = getLinkMode(elem);
+ if (linkGeneric == ModelLink.EXTERNAL) {
+ autoClasify[indexAutoClasify++] = StateLoad.ARRAY;
+ String localName = name;
+ if (name.endsWith("s")) {
+ localName = name.substring(0, name.length()-1);
+ }
+ String tmpVariable = "tmp_" + Integer.toString(count);
+ query.append(" (SELECT GROUP_CONCAT(");
+ query.append(tmpVariable);
+ query.append(".");
+ query.append(localName);
+ query.append("_id SEPARATOR '-') FROM ");
+ query.append(tableName);
+ query.append("_link_");
+ query.append(localName);
+ query.append(" ");
+ query.append(tmpVariable);
+ query.append(" WHERE ");
+ query.append(tmpVariable);
+ query.append(".deleted = false AND ");
+ query.append(tableName);
+ query.append(".id = ");
+ query.append(tmpVariable);
+ query.append(".");
+ query.append(tableName);
+ query.append("_id GROUP BY ");
+ query.append(tmpVariable);
+ query.append(".");
+ query.append(tableName);
+ query.append("_id ) AS ");
+ query.append(name);
+ query.append(" ");
+ /*
+ " (SELECT GROUP_CONCAT(tmp.data_id SEPARATOR '-')" +
+ " FROM cover_link_node tmp" +
+ " WHERE tmp.deleted = false" +
+ " AND node.id = tmp.node_id" +
+ " GROUP BY tmp.node_id) AS covers" +
+ */
+ } else {
+ if (linkGeneric == ModelLink.NONE) {
+ autoClasify[indexAutoClasify++] = StateLoad.NORMAL;
+ } else {
+ autoClasify[indexAutoClasify++] = StateLoad.ARRAY;
+ }
+ query.append(" ");
+ query.append(tableName);
+ query.append(".");
+ query.append(name);
+ }
+ }
+ query.append(" FROM `");
+ query.append(tableName);
+ query.append("` ");
+ query.append(" WHERE ");
+ //query.append(tableName);
+ //query.append(".");
+ //query.append(primaryKeyField.getName());
+ //query.append(" = ?");
+ //query.append(" AND ");
+ query.append(tableName);
+ query.append(".deleted = false ");
+ firstField = true;
+ System.out.println("generate the querry: '" + query.toString() + "'");
+ System.out.println("request get " + clazz.getCanonicalName() + " prepare @" + getCurrentTimeStamp());
+ // prepare the request:
+ PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
+
+ System.out.println("request get " + clazz.getCanonicalName() + " query @" + getCurrentTimeStamp());
+ // execute the request
+ ResultSet rs = ps.executeQuery();
+ System.out.println("request get " + clazz.getCanonicalName() + " transform @" + getCurrentTimeStamp());
+
+ while (rs.next()) {
+ indexAutoClasify = 0;
+ Object data = clazz.getConstructors()[0].newInstance();
+ count = 1;
+ for (Field elem : clazz.getFields()) {
+ /*
+ boolean notRead = elem.getDeclaredAnnotationsByType(SQLNotRead.class).length != 0;
+ */
+ boolean notRead = autoClasify[indexAutoClasify] == StateLoad.DISABLE;
+ if (!fool && notRead) {
+ indexAutoClasify++;
+ continue;
+ }
+ //String name = elem.getName();
+ //boolean linkGeneric = elem.getDeclaredAnnotationsByType(SQLTableLinkGeneric.class).length != 0;
+ boolean linkGeneric = autoClasify[indexAutoClasify] == StateLoad.ARRAY;
+ if (linkGeneric) {
+ List idList = getListOfIds(rs, count);
+ elem.set(data, idList);
+ } else {
+ setValueFromDb(elem.getType(), data, count, elem, rs);
+ }
+ indexAutoClasify++;
+ count++;
+ }
+ //System.out.println("Read: " + (T)data);
+ out.add((T)data);
+ }
+
+ System.out.println("request get " + clazz.getCanonicalName() + " ready @" + getCurrentTimeStamp());
+
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ entry.disconnect();
+ entry = null;
+ return out;
+ }
+
+ public static void addLink(Class> clazz, long localKey, String table, long remoteKey) throws Exception {
+ String tableName = getTableName(clazz);
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ long uniqueSQLID = -1;
+ // real add in the BDD:
+ try {
+ // prepare the request:
+ String query = "INSERT INTO " + tableName + "_link_" + table + " (create_date, modify_date, " + tableName + "_id, " + table + "_id)" +
+ " VALUES (now(3), now(3), ?, ?)";
+ PreparedStatement ps = entry.connection.prepareStatement(query,
+ Statement.RETURN_GENERATED_KEYS);
+ int iii = 1;
+ ps.setLong(iii++, localKey);
+ ps.setLong(iii++, remoteKey);
+ // execute the request
+ int affectedRows = ps.executeUpdate();
+ if (affectedRows == 0) {
+ throw new SQLException("Creating data failed, no rows affected.");
+ }
+ // retreive uid inserted
+ try (ResultSet generatedKeys = ps.getGeneratedKeys()) {
+ if (generatedKeys.next()) {
+ uniqueSQLID = generatedKeys.getLong(1);
+ } else {
+ throw new SQLException("Creating user failed, no ID obtained (1).");
+ }
+ } catch (Exception ex) {
+ System.out.println("Can not get the UID key inserted ... ");
+ ex.printStackTrace();
+ throw new SQLException("Creating user failed, no ID obtained (2).");
+ }
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ throw new ExceptionDBInterface(500, "SQL error: " + ex.getMessage());
+ } finally {
+ entry.disconnect();
+ }
+ }
+ public static void removeLink(Class> clazz, long localKey, String table, long remoteKey) throws Exception {
+ String tableName = getTableName(clazz);
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ String query = "UPDATE `" + tableName + "_link_" + table + "` SET `modify_date`=now(3), `deleted`=true WHERE `" + tableName + "_id` = ? AND `" + table + "_id` = ?";
+ try {
+ PreparedStatement ps = entry.connection.prepareStatement(query);
+ int iii = 1;
+ ps.setLong(iii++, localKey);
+ ps.setLong(iii++, remoteKey);
+ ps.executeUpdate();
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ throw new ExceptionDBInterface(500, "SQL error: " + ex.getMessage());
+ } finally {
+ entry.disconnect();
+ }
+ }
+
+ /**
+ * extract a list of "-" separated element from a SQL input data.
+ * @param rs Result Set of the BDD
+ * @param iii Id in the result set
+ * @return The list of Long value
+ * @throws SQLException if an error is generated in the sql request.
+ */
+ protected static List getListOfIds(ResultSet rs, int iii) throws SQLException {
+ String trackString = rs.getString(iii);
+ if (rs.wasNull()) {
+ return null;
+ }
+ List out = new ArrayList<>();
+ String[] elements = trackString.split("-");
+ for (String elem : elements) {
+ Long tmp = Long.parseLong(elem);
+ out.add(tmp);
+ }
+ return out;
+ }
+ /**
+ * Convert the list if external Ids in a string '-' separated
+ * @param ids List of value (null are removed)
+ * @return '-' string separated
+ */
+ protected static String getStringOfIds(List ids) {
+ List tmp = new ArrayList<>();
+ for (Long elem : ids) {
+ tmp.add(elem);
+ }
+ return tmp.stream().map(x->String.valueOf(x)).collect(Collectors.joining("-"));
+ }
+
+
+ public static void delete(Class> clazz, long id) throws Exception {
+ // TODO: I am not sure this is a real good idea.
+ }
+ public static void setDelete(Class> clazz, long id) throws Exception {
+ String tableName = getTableName(clazz);
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ String query = "UPDATE `" + tableName + "` SET `modify_date`=now(3), `deleted`=true WHERE `id` = ?";
+ try {
+ PreparedStatement ps = entry.connection.prepareStatement(query);
+ int iii = 1;
+ ps.setLong(iii++, id);
+ ps.executeUpdate();
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ throw new ExceptionDBInterface(500, "SQL error: " + ex.getMessage());
+ } finally {
+ entry.disconnect();
+ }
+ }
+
+ public static String createTable(Class> clazz) throws Exception {
+ String tableName = getTableName(clazz);
+ boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
+ StringBuilder out = new StringBuilder();
+ StringBuilder otherTable = new StringBuilder();
+ // Drop Table
+ if (createIfNotExist) {
+ out.append("DROP TABLE IF EXISTS `");
+ out.append(tableName);
+ out.append("`;\n");
+ }
+ // create Table:
+ out.append("CREATE TABLE `");
+ out.append(tableName);
+ out.append("` (");
+ boolean firstField = true;
+ System.out.println("===> TABLE `" + tableName + "`");
+ String primaryKeyValue = null;
+ for (Field elem : clazz.getFields()) {
+
+ String name = elem.getName();
+ Integer limitSize = getLimitSize(elem);
+ boolean notNull = elem.getDeclaredAnnotationsByType(SQLNotNull.class).length != 0;
+ boolean autoIncrement = elem.getDeclaredAnnotationsByType(SQLAutoIncrement.class).length != 0;
+
+ boolean primaryKey = elem.getDeclaredAnnotationsByType(SQLPrimaryKey.class).length != 0;
+ //boolean sqlNotRead = elem.getDeclaredAnnotationsByType(SQLNotRead.class).length != 0;
+ boolean createTime = elem.getDeclaredAnnotationsByType(SQLCreateTime.class).length != 0;
+ boolean updateTime = elem.getDeclaredAnnotationsByType(SQLUpdateTime.class).length != 0;
+ ModelLink linkGeneric = getLinkMode(elem);
+ String comment = getComment(elem);
+ String defaultValue = getDefault(elem);
+ //System.out.println(" ==> elem `" + name + "` primaryKey=" + primaryKey + " linkGeneric=" + linkGeneric);
+
+
+ if (primaryKey) {
+ primaryKeyValue = name;
+ }
+ // special case with external link table:
+ if (linkGeneric == ModelLink.EXTERNAL) {
+ String localName = name;
+ if (name.endsWith("s")) {
+ localName = name.substring(0, name.length()-1);
+ }
+ if (createIfNotExist) {
+ otherTable.append("DROP TABLE IF EXISTS `");
+ otherTable.append(tableName);
+ otherTable.append("_link_");
+ otherTable.append(localName);
+ otherTable.append("`;\n");
+ }
+ otherTable.append("CREATE TABLE `");
+ otherTable.append(tableName);
+ otherTable.append("_link_");
+ otherTable.append(localName);
+ otherTable.append("`(\n");
+ otherTable.append("\t\t`id` bigint NOT NULL AUTO_INCREMENT,\n");
+ otherTable.append("\t\t`deleted` tinyint(1) NOT NULL DEFAULT '0',\n");
+ otherTable.append("\t\t`create_date` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n");
+ otherTable.append("\t\t`modify_date` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n");
+ otherTable.append("\t\t`");
+ otherTable.append(tableName);
+ otherTable.append("_id` bigint NOT NULL,\n");
+ otherTable.append("\t\t`");
+ otherTable.append(localName);
+ otherTable.append("_id` bigint NOT NULL,\n");
+ otherTable.append("\tPRIMARY KEY (`id`)\n");
+ otherTable.append("\t) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n\n");
+ } else {
+ if (firstField) {
+ out.append("\n\t\t`");
+ firstField = false;
+ } else {
+ out.append(",\n\t\t`");
+ }
+ out.append(name);
+ out.append("` ");
+ String typeValue = null;
+ if (linkGeneric == ModelLink.INTERNAL) {
+ typeValue = convertTypeInSQL(String.class);
+ out.append(typeValue);
+ } else {
+ typeValue = convertTypeInSQL(elem.getType());
+ if (typeValue.equals("text")) {
+ if (limitSize != null) {
+ out.append("varchar(");
+ out.append(limitSize);
+ out.append(")");
+ } else {
+ out.append("text CHARACTER SET utf8");
+ }
+ } else {
+ out.append(typeValue);
+ }
+ }
+ out.append(" ");
+ if (notNull) {
+ out.append("NOT NULL ");
+ if (defaultValue == null) {
+ if (updateTime || createTime) {
+ out.append("DEFAULT CURRENT_TIMESTAMP(3) ");
+ }
+ } else {
+ out.append("DEFAULT ");
+ out.append(defaultValue);
+ out.append(" ");
+ }
+ } else if (defaultValue == null) {
+ if (updateTime || createTime) {
+ out.append("DEFAULT CURRENT_TIMESTAMP(3) ");
+ } else {
+ out.append("DEFAULT NULL ");
+ }
+ } else {
+ out.append("DEFAULT ");
+ out.append(defaultValue);
+ out.append(" ");
+
+ }
+ if (autoIncrement) {
+ out.append("AUTO_INCREMENT ");
+ }
+
+ if (comment != null) {
+ out.append("COMMENT '");
+ out.append(comment.replaceAll("'", "\'"));
+ out.append("' ");
+ }
+ }
+ }
+ if (primaryKeyValue != null) {
+ out.append(",\n\tPRIMARY KEY (`");
+ out.append(primaryKeyValue);
+ out.append("`)");
+ }
+ out.append("\n\t) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n");
+ return out.toString() + otherTable.toString();
+ }
+
+
+ public static String getTableName(final Class> element) throws Exception {
+ final Annotation[] annotation = element.getDeclaredAnnotationsByType(SQLTableName.class);
+ if (annotation.length == 0) {
+ return null;
+ }
+ if (annotation.length > 1) {
+ throw new Exception("Must not have more than 1 element @AknotDescription on " + element.getClass().getCanonicalName());
+ }
+ final String tmp = ((SQLTableName) annotation[0]).value();
+ if (tmp == null) {
+ return null;
+ }
+ return tmp;
+ }
+ public static String getComment(final Field element) throws Exception {
+ final Annotation[] annotation = element.getDeclaredAnnotationsByType(SQLComment.class);
+ if (annotation.length == 0) {
+ return null;
+ }
+ if (annotation.length > 1) {
+ throw new Exception("Must not have more than 1 element @AknotDescription on " + element.getClass().getCanonicalName());
+ }
+ final String tmp = ((SQLComment) annotation[0]).value();
+ if (tmp == null) {
+ return null;
+ }
+ return tmp;
+ }
+ public static String getDefault(final Field element) throws Exception {
+ final Annotation[] annotation = element.getDeclaredAnnotationsByType(SQLDefault.class);
+ if (annotation.length == 0) {
+ return null;
+ }
+ if (annotation.length > 1) {
+ throw new Exception("Must not have more than 1 element @AknotDescription on " + element.getClass().getCanonicalName());
+ }
+ final String tmp = ((SQLDefault) annotation[0]).value();
+ if (tmp == null) {
+ return null;
+ }
+ return tmp;
+ }
+ public static Integer getLimitSize(final Field element) throws Exception {
+ final Annotation[] annotation = element.getDeclaredAnnotationsByType(SQLLimitSize.class);
+ if (annotation.length == 0) {
+ return null;
+ }
+ if (annotation.length > 1) {
+ throw new Exception("Must not have more than 1 element @AknotDescription on " + element.getClass().getCanonicalName());
+ }
+ return ((SQLLimitSize) annotation[0]).value();
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/kar/archidata/UpdateJwtPublicKey.java b/src/org/kar/archidata/UpdateJwtPublicKey.java
new file mode 100644
index 0000000..a8e0594
--- /dev/null
+++ b/src/org/kar/archidata/UpdateJwtPublicKey.java
@@ -0,0 +1,34 @@
+package org.kar.archidata;
+
+import org.kar.archidata.util.ConfigBaseVariable;
+import org.kar.archidata.util.JWTWrapper;
+
+public class UpdateJwtPublicKey extends Thread {
+ boolean kill = false;
+ public void run() {
+ try {
+ Thread.sleep(1000*20, 0);
+ } catch (InterruptedException e2) {
+ // TODO Auto-generated catch block
+ e2.printStackTrace();
+ }
+ while (this.kill == false) {
+ // need to uppgrade when server call us...
+ try {
+ JWTWrapper.initLocalTokenRemote(ConfigBaseVariable.getSSOAddress(), "archidata");
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ System.out.println("Can not retreive the basic tocken");
+ return;
+ }
+ try {
+ Thread.sleep(1000*60*5, 0);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ public void kill() {
+ this.kill = true;
+ }
+}
\ No newline at end of file
diff --git a/src/org/kar/archidata/UserDB.java b/src/org/kar/archidata/UserDB.java
new file mode 100755
index 0000000..d638e1e
--- /dev/null
+++ b/src/org/kar/archidata/UserDB.java
@@ -0,0 +1,133 @@
+package org.kar.archidata;
+
+import org.kar.archidata.db.DBEntry;
+import org.kar.archidata.model.User;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+public class UserDB {
+
+ public UserDB() {
+ }
+
+ public static User getUsers(long userId) throws Exception {
+ return SqlWrapper.get(User.class, userId);
+ /*
+ DBEntry entry = new DBEntry(WebLauncher.dbConfig);
+ String query = "SELECT * FROM user WHERE id = ?";
+ try {
+ PreparedStatement ps = entry.connection.prepareStatement(query);
+ ps.setLong(1, userId);
+ ResultSet rs = ps.executeQuery();
+ if (rs.next()) {
+ User out = new User(rs);
+ entry.disconnect();
+ return out;
+ }
+ } catch (SQLException throwables) {
+ throwables.printStackTrace();
+ }
+ entry.disconnect();
+ return null;
+ */
+ }
+
+
+ public static User getUserOrCreate(long userId, String userLogin) throws Exception {
+ User user = getUsers(userId);
+ if (user != null) {
+ /*
+ boolean blocked = false;
+ boolean removed = false;
+ if (user.email != userOAuth.email || user.login != userOAuth.login || user.blocked != blocked || user.removed != removed) {
+ updateUsersInfoFromOAuth(userOAuth.id, userOAuth.email, userOAuth.login, blocked, removed);
+ } else {
+ updateUsersConnectionTime(userOAuth.id);
+ }
+ return getUsers(userOAuth.id);
+ */
+ return user;
+ }
+ createUsersInfoFromOAuth(userId, userLogin);
+ return getUsers(userId);
+ }
+/*
+ private static void updateUsersConnectionTime(long userId) {
+ DBEntry entry = new DBEntry(WebLauncher.dbConfig);
+ String query = "UPDATE `user` SET `lastConnection`=now(3) WHERE `id` = ?";
+ try {
+ PreparedStatement ps = entry.connection.prepareStatement(query);
+ ps.setLong(1, userId);
+ ps.executeUpdate();
+ } catch (SQLException throwables) {
+ throwables.printStackTrace();
+ }
+ entry.disconnect();
+ }
+
+ private static void updateUsersInfoFromOAuth(long userId, String email, String login, boolean blocked, boolean removed) {
+ DBEntry entry = new DBEntry(WebLauncher.dbConfig);
+ String query = "UPDATE `user` SET `login`=?, `email`=?, `lastConnection`=now(3), `blocked`=?, `removed`=? WHERE id = ?";
+ try {
+ PreparedStatement ps = entry.connection.prepareStatement(query);
+ ps.setString(1, login);
+ ps.setString(2, email);
+ ps.setString(3, blocked ? "TRUE" : "FALSE");
+ ps.setString(4, removed ? "TRUE" : "FALSE");
+ ps.setLong(5, userId);
+ ps.executeUpdate();
+ } catch (SQLException throwables) {
+ throwables.printStackTrace();
+ }
+ entry.disconnect();
+ }
+ */
+
+ private static void createUsersInfoFromOAuth(long userId, String login) {
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ String query = "INSERT INTO `user` (`id`, `login`, `lastConnection`, `admin`, `blocked`, `removed`) VALUE (?,?,now(3),'0','0','0')";
+ try {
+ PreparedStatement ps = entry.connection.prepareStatement(query);
+ ps.setLong(1, userId);
+ ps.setString(2, login);
+ ps.executeUpdate();
+ } catch (SQLException throwables) {
+ throwables.printStackTrace();
+ }
+ entry.disconnect();
+ }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/kar/archidata/annotation/PermitTokenInURI.java b/src/org/kar/archidata/annotation/PermitTokenInURI.java
new file mode 100644
index 0000000..95a1117
--- /dev/null
+++ b/src/org/kar/archidata/annotation/PermitTokenInURI.java
@@ -0,0 +1,15 @@
+package org.kar.archidata.annotation;
+
+import javax.ws.rs.NameBinding;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@NameBinding
+@Retention(RUNTIME)
+@Target({METHOD})
+public @interface PermitTokenInURI {
+}
diff --git a/src/org/kar/archidata/annotation/SQLAutoIncrement.java b/src/org/kar/archidata/annotation/SQLAutoIncrement.java
new file mode 100644
index 0000000..8c22c7d
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLAutoIncrement.java
@@ -0,0 +1,12 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLAutoIncrement {
+
+}
diff --git a/src/org/kar/archidata/annotation/SQLComment.java b/src/org/kar/archidata/annotation/SQLComment.java
new file mode 100644
index 0000000..9bfa21f
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLComment.java
@@ -0,0 +1,14 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.TYPE, ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLComment {
+
+ String value();
+
+}
diff --git a/src/org/kar/archidata/annotation/SQLCreateTime.java b/src/org/kar/archidata/annotation/SQLCreateTime.java
new file mode 100644
index 0000000..51dfdec
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLCreateTime.java
@@ -0,0 +1,12 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLCreateTime {
+
+}
diff --git a/src/org/kar/archidata/annotation/SQLDefault.java b/src/org/kar/archidata/annotation/SQLDefault.java
new file mode 100644
index 0000000..ec88f2e
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLDefault.java
@@ -0,0 +1,14 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.TYPE, ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLDefault {
+
+ String value();
+
+}
diff --git a/src/org/kar/archidata/annotation/SQLIfNotExists.java b/src/org/kar/archidata/annotation/SQLIfNotExists.java
new file mode 100644
index 0000000..9b185d6
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLIfNotExists.java
@@ -0,0 +1,12 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLIfNotExists {
+
+}
diff --git a/src/org/kar/archidata/annotation/SQLLimitSize.java b/src/org/kar/archidata/annotation/SQLLimitSize.java
new file mode 100644
index 0000000..5ed10c4
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLLimitSize.java
@@ -0,0 +1,12 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLLimitSize {
+ int value();
+}
diff --git a/src/org/kar/archidata/annotation/SQLNotNull.java b/src/org/kar/archidata/annotation/SQLNotNull.java
new file mode 100644
index 0000000..07a20aa
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLNotNull.java
@@ -0,0 +1,12 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLNotNull {
+
+}
diff --git a/src/org/kar/archidata/annotation/SQLNotRead.java b/src/org/kar/archidata/annotation/SQLNotRead.java
new file mode 100644
index 0000000..50bb67b
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLNotRead.java
@@ -0,0 +1,12 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLNotRead {
+
+}
diff --git a/src/org/kar/archidata/annotation/SQLPrimaryKey.java b/src/org/kar/archidata/annotation/SQLPrimaryKey.java
new file mode 100644
index 0000000..60944cf
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLPrimaryKey.java
@@ -0,0 +1,12 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLPrimaryKey {
+
+}
diff --git a/src/org/kar/archidata/annotation/SQLTableLinkGeneric.java b/src/org/kar/archidata/annotation/SQLTableLinkGeneric.java
new file mode 100644
index 0000000..b8f9632
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLTableLinkGeneric.java
@@ -0,0 +1,20 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLTableLinkGeneric {
+ public enum ModelLink {
+ NONE,
+ INTERNAL,
+ EXTERNAL
+ };
+ ModelLink value() default ModelLink.EXTERNAL;
+}
diff --git a/src/org/kar/archidata/annotation/SQLTableName.java b/src/org/kar/archidata/annotation/SQLTableName.java
new file mode 100644
index 0000000..5902edc
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLTableName.java
@@ -0,0 +1,14 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLTableName {
+
+ String value();
+
+}
diff --git a/src/org/kar/archidata/annotation/SQLUpdateTime.java b/src/org/kar/archidata/annotation/SQLUpdateTime.java
new file mode 100644
index 0000000..fe2ecad
--- /dev/null
+++ b/src/org/kar/archidata/annotation/SQLUpdateTime.java
@@ -0,0 +1,12 @@
+package org.kar.archidata.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SQLUpdateTime {
+
+}
diff --git a/src/org/kar/archidata/api/FrontGeneric.java b/src/org/kar/archidata/api/FrontGeneric.java
new file mode 100644
index 0000000..7f699af
--- /dev/null
+++ b/src/org/kar/archidata/api/FrontGeneric.java
@@ -0,0 +1,104 @@
+package org.kar.archidata.api;
+
+import java.io.File;
+import java.util.List;
+
+import javax.annotation.security.PermitAll;
+import javax.ws.rs.*;
+import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.PathSegment;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+
+
+public class FrontGeneric {
+
+ protected String baseFrontFolder = "/data/front";
+
+ private String getExtension(String filename) {
+ if (filename.contains(".")) {
+ return filename.substring(filename.lastIndexOf(".") + 1);
+ }
+ return "";
+ }
+ private Response retrive(String fileName) throws Exception {
+ String filePathName = baseFrontFolder + File.separator + fileName;
+ String extention = getExtension(filePathName);
+ String mineType = null;
+ System.out.println("try retrive : '" + filePathName + "' '" + extention + "'");
+ if (extention.length() !=0 && extention.length() <= 5) {
+ if (extention.equalsIgnoreCase("jpg") || extention.equalsIgnoreCase("jpeg")) {
+ mineType = "image/jpeg";
+ } else if (extention.equalsIgnoreCase("gif")) {
+ mineType = "image/gif";
+ } else if (extention.equalsIgnoreCase("png")) {
+ mineType = "image/png";
+ } else if (extention.equalsIgnoreCase("svg")) {
+ mineType = "image/svg+xml";
+ } else if (extention.equalsIgnoreCase("webp")) {
+ mineType = "image/webp";
+ } else if (extention.equalsIgnoreCase("js")) {
+ mineType = "application/javascript";
+ } else if (extention.equalsIgnoreCase("json")) {
+ mineType = "application/json";
+ } else if (extention.equalsIgnoreCase("ico")) {
+ mineType = "image/x-icon";
+ } else if (extention.equalsIgnoreCase("html")) {
+ mineType = "text/html";
+ } else if (extention.equalsIgnoreCase("css")) {
+ mineType = "text/css";
+ } else {
+ return Response.status(403).
+ entity("Not supported model: '" + fileName + "'").
+ type("text/plain").
+ build();
+ }
+ } else {
+ mineType = "text/html";
+ filePathName = baseFrontFolder + File.separator + "index.html";
+ }
+ System.out.println(" ==> '" + filePathName + "'");
+ // reads input image
+ File download = new File(filePathName);
+ if (!download.exists()) {
+ return Response.status(404).
+ entity("Not Found: '" + fileName + "' extension='" + extention + "'").
+ type("text/plain").
+ build();
+ }
+ ResponseBuilder response = Response.ok((Object)download);
+ // use this if I want to download the file:
+ //response.header("Content-Disposition", "attachment; filename=" + fileName);
+ CacheControl cc = new CacheControl();
+ cc.setMaxAge(60);
+ cc.setNoCache(false);
+ response.cacheControl(cc);
+ response.type(mineType);
+
+ return response.build();
+ }
+
+ @GET
+ @PermitAll()
+ //@Produces(MediaType.APPLICATION_OCTET_STREAM)
+ //@CacheMaxAge(time = 1, unit = TimeUnit.DAYS)
+ public Response retrive0() throws Exception {
+ return retrive("index.html");
+ }
+
+ @GET
+ @Path("{any: .*}")
+ @PermitAll()
+ //@Produces(MediaType.APPLICATION_OCTET_STREAM)
+ //@CacheMaxAge(time = 10, unit = TimeUnit.DAYS)
+ public Response retrive1(@PathParam("any") List segments) throws Exception {
+ String filename = "";
+ for (PathSegment elem: segments) {
+ if (!filename.isEmpty()) {
+ filename += File.separator;
+ }
+ filename += elem.getPath();
+ }
+ return retrive(filename);
+ }
+}
diff --git a/src/org/kar/archidata/db/DBConfig.java b/src/org/kar/archidata/db/DBConfig.java
new file mode 100644
index 0000000..577a658
--- /dev/null
+++ b/src/org/kar/archidata/db/DBConfig.java
@@ -0,0 +1,60 @@
+package org.kar.archidata.db;
+
+public class DBConfig {
+ private final String hostname;
+ private final int port;
+ private final String login;
+ private final String password;
+ private final String dbName;
+
+ public DBConfig(String hostname, Integer port, String login, String password, String dbName) {
+ if (hostname == null) {
+ this.hostname = "localhost";
+ } else {
+ this.hostname = hostname;
+ }
+ if (port == null) {
+ this.port = 3306;
+ } else {
+ this.port = port;
+ }
+ this.login = login;
+ this.password = password;
+ this.dbName = dbName;
+ }
+
+ @Override
+ public String toString() {
+ return "DBConfig{" +
+ "hostname='" + hostname + '\'' +
+ ", port=" + port +
+ ", login='" + login + '\'' +
+ ", password='" + password + '\'' +
+ ", dbName='" + dbName + '\'' +
+ '}';
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getLogin() {
+ return login;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getDbName() {
+ return dbName;
+ }
+
+ public String getUrl() {
+ return "jdbc:mysql://" + this.hostname + ":" + this.port + "/" + this.dbName + "?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC";
+ }
+}
diff --git a/src/org/kar/archidata/db/DBEntry.java b/src/org/kar/archidata/db/DBEntry.java
new file mode 100644
index 0000000..32c9f05
--- /dev/null
+++ b/src/org/kar/archidata/db/DBEntry.java
@@ -0,0 +1,45 @@
+package org.kar.archidata.db;
+
+import org.kar.archidata.model.User;
+
+import java.sql.*;
+
+public class DBEntry {
+ public DBConfig config;
+ public Connection connection;
+
+ public DBEntry(DBConfig config) {
+ this.config = config;
+ connect();
+ }
+
+ public void connect() {
+ try {
+ connection = DriverManager.getConnection(config.getUrl(), config.getLogin(), config.getPassword());
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+
+ }
+
+ public void disconnect() {
+ try {
+ //connection.commit();
+ connection.close();
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ }
+/*
+ public void test() throws SQLException {
+ String query = "SELECT * FROM user";
+ Statement st = connection.createStatement();
+ ResultSet rs = st.executeQuery(query);
+ System.out.println("List of user:");
+ if (rs.next()) {
+ User user = new User(rs);
+ System.out.println(" - " + user);
+ }
+ }
+ */
+}
diff --git a/src/org/kar/archidata/filter/AuthenticationFilter.java b/src/org/kar/archidata/filter/AuthenticationFilter.java
new file mode 100644
index 0000000..7a93842
--- /dev/null
+++ b/src/org/kar/archidata/filter/AuthenticationFilter.java
@@ -0,0 +1,198 @@
+package org.kar.archidata.filter;
+
+import java.lang.reflect.Method;
+import javax.annotation.security.DenyAll;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.PathSegment;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+import org.kar.archidata.UserDB;
+import org.kar.archidata.annotation.PermitTokenInURI;
+import org.kar.archidata.model.User;
+import org.kar.archidata.model.UserSmall;
+import org.kar.archidata.util.JWTWrapper;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map.Entry;
+// https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey
+// https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey/45814178#45814178
+// https://stackoverflow.com/questions/32817210/how-to-access-jersey-resource-secured-by-rolesallowed
+
+//@PreMatching
+@Provider
+@Priority(Priorities.AUTHENTICATION)
+public class AuthenticationFilter implements ContainerRequestFilter {
+ @Context
+ private ResourceInfo resourceInfo;
+
+ private static final String AUTHENTICATION_SCHEME = "Yota";
+
+ @Override
+ public void filter(ContainerRequestContext requestContext) throws IOException {
+ /*
+ System.out.println("-----------------------------------------------------");
+ System.out.println("---- Check if have authorization ----");
+ System.out.println("-----------------------------------------------------");
+ System.out.println(" for:" + requestContext.getUriInfo().getPath());
+ */
+ Method method = resourceInfo.getResourceMethod();
+ // Access denied for all
+ if(method.isAnnotationPresent(DenyAll.class)) {
+ System.out.println(" ==> deny all " + requestContext.getUriInfo().getPath());
+ requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access blocked !!!").build());
+ return;
+ }
+
+ //Access allowed for all
+ if( method.isAnnotationPresent(PermitAll.class)) {
+ System.out.println(" ==> permit all " + requestContext.getUriInfo().getPath());
+ // no control ...
+ return;
+ }
+ // this is a security guard, all the API must define their access level:
+ if(!method.isAnnotationPresent(RolesAllowed.class)) {
+ System.out.println(" ==> missin @RolesAllowed " + requestContext.getUriInfo().getPath());
+ requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access ILLEGAL !!!").build());
+ return;
+
+ }
+
+ // Get the Authorization header from the request
+ String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
+ //System.out.println("authorizationHeader: " + authorizationHeader);
+ if(authorizationHeader == null && method.isAnnotationPresent(PermitTokenInURI.class)) {
+ MultivaluedMap quaryparam = requestContext.getUriInfo().getQueryParameters();
+ for (Entry> item: quaryparam.entrySet()) {
+ if (item.getKey().equals(HttpHeaders.AUTHORIZATION)) {
+ if (!item.getValue().isEmpty()) {
+ authorizationHeader = item.getValue().get(0);
+ }
+ break;
+ }
+ }
+ }
+ //System.out.println("authorizationHeader: " + authorizationHeader);
+
+
+ /*
+ System.out.println(" -------------------------------");
+ // this get the parameters inside the pre-parsed element in the request ex: @Path("thumbnail/{id}") generate a map with "id"
+ MultivaluedMap pathparam = requestContext.getUriInfo().getPathParameters();
+ for (Entry> item: pathparam.entrySet()) {
+ System.out.println(" param: " + item.getKey() + " ==>" + item.getValue());
+ }
+ System.out.println(" -------------------------------");
+ // need to add "@QueryParam("p") String token, " in the model
+ //MultivaluedMap quaryparam = requestContext.getUriInfo().getQueryParameters();
+ for (Entry> item: quaryparam.entrySet()) {
+ System.out.println(" query: " + item.getKey() + " ==>" + item.getValue());
+ }
+ System.out.println(" -------------------------------");
+ List segments = requestContext.getUriInfo().getPathSegments();
+ for (final PathSegment item: segments) {
+ System.out.println(" query: " + item.getPath() + " ==>" + item.getMatrixParameters());
+ }
+ System.out.println(" -------------------------------");
+ MultivaluedMap headers = requestContext.getHeaders();
+ for (Entry> item: headers.entrySet()) {
+ System.out.println(" headers: " + item.getKey() + " ==>" + item.getValue());
+ }
+ System.out.println(" -------------------------------");
+ */
+ // Validate the Authorization header data Model "Yota userId:token"
+ if (!isTokenBasedAuthentication(authorizationHeader)) {
+ System.out.println("REJECTED unauthorized: " + requestContext.getUriInfo().getPath());
+ abortWithUnauthorized(requestContext);
+ return;
+ }
+ // check JWT token (basic:)
+
+ // Extract the token from the Authorization header (Remove "Yota ")
+ String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim();
+ System.out.println("token: " + token);
+
+
+ User user = null;
+ try {
+ user = validateToken(token);
+ } catch (Exception e) {
+ abortWithUnauthorized(requestContext);
+ }
+ if (user == null) {
+ abortWithUnauthorized(requestContext);
+ }
+ // create the security context model:
+ String scheme = requestContext.getUriInfo().getRequestUri().getScheme();
+ MySecurityContext userContext = new MySecurityContext(user, scheme);
+ // retrieve the allowed right:
+ RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
+ List roles = Arrays.asList(rolesAnnotation.value());
+ // check if the user have the right:
+ boolean haveRight = false;
+ for (String role : roles) {
+ if (userContext.isUserInRole(role)) {
+ haveRight = true;
+ break;
+ }
+ }
+
+ //Is user valid?
+ if( ! haveRight) {
+ System.out.println("REJECTED not enought right : " + requestContext.getUriInfo().getPath() + " require: " + roles);
+ requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Not enought RIGHT !!!").build());
+ return;
+ }
+ requestContext.setSecurityContext(userContext);
+ System.out.println("Get local user : " + user);
+ }
+
+ private boolean isTokenBasedAuthentication(String authorizationHeader) {
+ // Check if the Authorization header is valid
+ // It must not be null and must be prefixed with "Bearer" plus a whitespace
+ // The authentication scheme comparison must be case-insensitive
+ return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
+ }
+
+ private void abortWithUnauthorized(ContainerRequestContext requestContext) {
+
+ // Abort the filter chain with a 401 status code response
+ // The WWW-Authenticate header is sent along with the response
+ requestContext.abortWith(
+ Response.status(Response.Status.UNAUTHORIZED)
+ .header(HttpHeaders.WWW_AUTHENTICATE,
+ AUTHENTICATION_SCHEME + " base64(HEADER).base64(CONTENT).base64(KEY)")
+ .build());
+ }
+
+ private User validateToken(String authorization) throws Exception {
+ System.out.println(" validate token : " + authorization);
+ JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth");
+ // check the token is valid !!! (signed and coherent issuer...
+ if (ret == null) {
+ System.out.println("The token is not valid: '" + authorization + "'");
+ return null;
+ }
+ // check userID
+ String userUID = ret.getSubject();
+ long id = Long.parseLong(userUID);
+ System.out.println("request user: '" + userUID + "'");
+ return UserDB.getUserOrCreate(id, (String)ret.getClaim("login") );
+ }
+}
diff --git a/src/org/kar/archidata/filter/CORSFilter.java b/src/org/kar/archidata/filter/CORSFilter.java
new file mode 100644
index 0000000..ce7f798
--- /dev/null
+++ b/src/org/kar/archidata/filter/CORSFilter.java
@@ -0,0 +1,25 @@
+package org.kar.archidata.filter;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.ext.Provider;
+import java.io.IOException;
+
+
+@Provider
+public class CORSFilter implements ContainerResponseFilter {
+
+ @Override
+ public void filter(ContainerRequestContext request,
+ ContainerResponseContext response) throws IOException {
+ //System.err.println("filter cors ..." + request.toString());
+
+ response.getHeaders().add("Access-Control-Allow-Origin", "*");
+ response.getHeaders().add("Access-Control-Allow-Headers", "*");
+ // "Origin, content-type, Content-type, Accept, authorization, mime-type, filename");
+ response.getHeaders().add("Access-Control-Allow-Credentials", "true");
+ response.getHeaders().add("Access-Control-Allow-Methods",
+ "GET, POST, PUT, DELETE, OPTIONS, HEAD");
+ }
+}
diff --git a/src/org/kar/archidata/filter/GenericContext.java b/src/org/kar/archidata/filter/GenericContext.java
new file mode 100644
index 0000000..faeb5c9
--- /dev/null
+++ b/src/org/kar/archidata/filter/GenericContext.java
@@ -0,0 +1,22 @@
+package org.kar.archidata.filter;
+
+import org.kar.archidata.model.User;
+
+import java.security.Principal;
+
+public class GenericContext implements Principal {
+
+ public User user;
+
+ public GenericContext(User user) {
+ this.user = user;
+ }
+
+ @Override
+ public String getName() {
+ if (user == null) {
+ return "???";
+ }
+ return user.login;
+ }
+}
diff --git a/src/org/kar/archidata/filter/MySecurityContext.java b/src/org/kar/archidata/filter/MySecurityContext.java
new file mode 100644
index 0000000..b61a564
--- /dev/null
+++ b/src/org/kar/archidata/filter/MySecurityContext.java
@@ -0,0 +1,47 @@
+package org.kar.archidata.filter;
+
+
+import org.kar.archidata.model.User;
+
+import javax.ws.rs.core.SecurityContext;
+import java.security.Principal;
+
+// https://simplapi.wordpress.com/2015/09/19/jersey-jax-rs-securitycontext-in-action/
+class MySecurityContext implements SecurityContext {
+
+ private final GenericContext contextPrincipale;
+ private final String sheme;
+
+ public MySecurityContext(User user, String sheme) {
+ this.contextPrincipale = new GenericContext(user);
+ this.sheme = sheme;
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ return contextPrincipale;
+ }
+
+ @Override
+ public boolean isUserInRole(String role) {
+ if (role.contentEquals("ADMIN")) {
+ return contextPrincipale.user.admin == true;
+ }
+ if (role.contentEquals("USER")) {
+ // if not an admin, this is a user...
+ return true; //contextPrincipale.user.admin == false;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isSecure() {
+ return true;
+ }
+
+ @Override
+ public String getAuthenticationScheme() {
+ return "Yota";
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/kar/archidata/filter/OptionFilter.java b/src/org/kar/archidata/filter/OptionFilter.java
new file mode 100644
index 0000000..bfe9527
--- /dev/null
+++ b/src/org/kar/archidata/filter/OptionFilter.java
@@ -0,0 +1,21 @@
+package org.kar.archidata.filter;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+import java.io.IOException;
+
+@Provider
+@PreMatching
+public class OptionFilter implements ContainerRequestFilter {
+ @Override
+ public void filter(ContainerRequestContext requestContext) throws IOException {
+ if (requestContext.getMethod().contentEquals("OPTIONS")) {
+ requestContext.abortWith(Response.status(Response.Status.NO_CONTENT).build());
+ }
+ }
+}
+
+
diff --git a/src/org/kar/archidata/internal/Log.java b/src/org/kar/archidata/internal/Log.java
new file mode 100644
index 0000000..d8545ff
--- /dev/null
+++ b/src/org/kar/archidata/internal/Log.java
@@ -0,0 +1,60 @@
+package org.kar.archidata.internal;
+
+//import io.scenarium.logger.LogLevel;
+//import io.scenarium.logger.Logger;
+
+public class Log {
+// private static final String LIB_NAME = "logger";
+// private static final String LIB_NAME_DRAW = Logger.getDrawableName(LIB_NAME);
+// private static final boolean PRINT_CRITICAL = Logger.getNeedPrint(LIB_NAME, LogLevel.CRITICAL);
+// private static final boolean PRINT_ERROR = Logger.getNeedPrint(LIB_NAME, LogLevel.ERROR);
+// private static final boolean PRINT_WARNING = Logger.getNeedPrint(LIB_NAME, LogLevel.WARNING);
+// private static final boolean PRINT_INFO = Logger.getNeedPrint(LIB_NAME, LogLevel.INFO);
+// private static final boolean PRINT_DEBUG = Logger.getNeedPrint(LIB_NAME, LogLevel.DEBUG);
+// private static final boolean PRINT_VERBOSE = Logger.getNeedPrint(LIB_NAME, LogLevel.VERBOSE);
+// private static final boolean PRINT_TODO = Logger.getNeedPrint(LIB_NAME, LogLevel.TODO);
+// private static final boolean PRINT_PRINT = Logger.getNeedPrint(LIB_NAME, LogLevel.PRINT);
+//
+// private Log() {}
+//
+// public static void print(String data) {
+// if (PRINT_PRINT)
+// Logger.print(LIB_NAME_DRAW, data);
+// }
+//
+// public static void todo(String data) {
+// if (PRINT_TODO)
+// Logger.todo(LIB_NAME_DRAW, data);
+// }
+//
+// public static void critical(String data) {
+// if (PRINT_CRITICAL)
+// Logger.critical(LIB_NAME_DRAW, data);
+// }
+//
+// public static void error(String data) {
+// if (PRINT_ERROR)
+// Logger.error(LIB_NAME_DRAW, data);
+// }
+//
+// public static void warning(String data) {
+// if (PRINT_WARNING)
+// Logger.warning(LIB_NAME_DRAW, data);
+// }
+//
+// public static void info(String data) {
+// if (PRINT_INFO)
+// Logger.info(LIB_NAME_DRAW, data);
+// }
+//
+// public static void debug(String data) {
+// if (PRINT_DEBUG)
+// Logger.debug(LIB_NAME_DRAW, data);
+// }
+//
+// public static void verbose(String data) {
+// if (PRINT_VERBOSE)
+// Logger.verbose(LIB_NAME_DRAW, data);
+// }
+
+}
diff --git a/src/org/kar/archidata/model/Data.java b/src/org/kar/archidata/model/Data.java
new file mode 100644
index 0000000..a34617a
--- /dev/null
+++ b/src/org/kar/archidata/model/Data.java
@@ -0,0 +1,32 @@
+package org.kar.archidata.model;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class Data {
+ public Long id;
+ public boolean deleted;
+ public String sha512;
+ public String mimeType;
+ public Long size;
+
+ public Data() {
+
+ }
+
+ public Data(ResultSet rs) {
+ int iii = 1;
+ try {
+ this.id = rs.getLong(iii++);
+ this.deleted = rs.getBoolean(iii++);
+ this.sha512 = rs.getString(iii++);
+ this.mimeType = rs.getString(iii++);
+ this.size = rs.getLong(iii++);
+ if (rs.wasNull()) {
+ this.size = null;
+ }
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ }
+}
diff --git a/src/org/kar/archidata/model/GenericTable.java b/src/org/kar/archidata/model/GenericTable.java
new file mode 100644
index 0000000..8134c8e
--- /dev/null
+++ b/src/org/kar/archidata/model/GenericTable.java
@@ -0,0 +1,35 @@
+package org.kar.archidata.model;
+
+import java.sql.Timestamp;
+
+import org.kar.archidata.annotation.SQLAutoIncrement;
+import org.kar.archidata.annotation.SQLComment;
+import org.kar.archidata.annotation.SQLCreateTime;
+import org.kar.archidata.annotation.SQLDefault;
+import org.kar.archidata.annotation.SQLNotNull;
+import org.kar.archidata.annotation.SQLNotRead;
+import org.kar.archidata.annotation.SQLPrimaryKey;
+import org.kar.archidata.annotation.SQLUpdateTime;
+
+public class GenericTable {
+ @SQLAutoIncrement // Add AUTO_INCREMENT modifier
+ @SQLPrimaryKey // Create a PRIMARY KEY based on this field
+ @SQLNotNull
+ @SQLComment("Primary key of the base")
+ public Long id = null;
+ @SQLNotRead
+ @SQLNotNull
+ @SQLDefault("'0'")
+ @SQLComment("When delete, they are not removed, they are just set in a deleted state")
+ public Boolean deleted = null;
+ @SQLNotRead
+ @SQLCreateTime
+ @SQLNotNull
+ @SQLComment("Create time of the object")
+ public Timestamp create_date = null;
+ @SQLNotRead
+ @SQLUpdateTime
+ @SQLNotNull
+ @SQLComment("When update the object")
+ public Timestamp modify_date = null;
+}
diff --git a/src/org/kar/archidata/model/State.java b/src/org/kar/archidata/model/State.java
new file mode 100644
index 0000000..bd3195e
--- /dev/null
+++ b/src/org/kar/archidata/model/State.java
@@ -0,0 +1,12 @@
+package org.kar.archidata.model;
+
+public enum State {
+ // User has remove his account
+ REMOVED,
+ // User has been blocked his account
+ BLOCKED,
+ // generic user
+ USER,
+ // Administrator
+ ADMIN
+}
diff --git a/src/org/kar/archidata/model/Token.java b/src/org/kar/archidata/model/Token.java
new file mode 100644
index 0000000..636cbfd
--- /dev/null
+++ b/src/org/kar/archidata/model/Token.java
@@ -0,0 +1,57 @@
+package org.kar.archidata.model;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/*
+CREATE TABLE `token` (
+ `id` bigint NOT NULL COMMENT 'Unique ID of the TOKEN' AUTO_INCREMENT PRIMARY KEY,
+ `userId` bigint NOT NULL COMMENT 'Unique ID of the user',
+ `token` varchar(128) COLLATE 'latin1_bin' NOT NULL COMMENT 'Token (can be not unique)',
+ `createTime` datetime NOT NULL COMMENT 'Time the token has been created',
+ `endValidityTime` datetime NOT NULL COMMENT 'Time of the token end validity'
+) AUTO_INCREMENT=10;
+
+ */
+public class Token {
+ public Long id;
+ public Long userId;
+ public String token;
+ public String createTime;
+ public String endValidityTime;
+
+ public Token() {
+ }
+
+ public Token(long id, long userId, String token, String createTime, String endValidityTime) {
+ this.id = id;
+ this.userId = userId;
+ this.token = token;
+ this.createTime = createTime;
+ this.endValidityTime = endValidityTime;
+ }
+
+ public Token(ResultSet rs) {
+ int iii = 1;
+ try {
+ this.id = rs.getLong(iii++);
+ this.userId = rs.getLong(iii++);
+ this.token = rs.getString(iii++);
+ this.createTime = rs.getString(iii++);
+ this.endValidityTime = rs.getString(iii++);
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Token{" +
+ "id=" + id +
+ ", userId=" + userId +
+ ", token='" + token + '\'' +
+ ", createTime=" + createTime +
+ ", endValidityTime=" + endValidityTime +
+ '}';
+ }
+}
diff --git a/src/org/kar/archidata/model/User.java b/src/org/kar/archidata/model/User.java
new file mode 100644
index 0000000..9fbf596
--- /dev/null
+++ b/src/org/kar/archidata/model/User.java
@@ -0,0 +1,53 @@
+package org.kar.archidata.model;
+
+/*
+CREATE TABLE `user` (
+ `id` bigint NOT NULL COMMENT 'table ID' AUTO_INCREMENT PRIMARY KEY,
+ `login` varchar(128) COLLATE 'utf8_general_ci' NOT NULL COMMENT 'login of the user',
+ `email` varchar(512) COLLATE 'utf8_general_ci' NOT NULL COMMENT 'email of the user',
+ `lastConnection` datetime NOT NULL COMMENT 'last connection time',
+ `admin` enum("TRUE", "FALSE") NOT NULL DEFAULT 'FALSE',
+ `blocked` enum("TRUE", "FALSE") NOT NULL DEFAULT 'FALSE',
+ `removed` enum("TRUE", "FALSE") NOT NULL DEFAULT 'FALSE',
+ `avatar` bigint DEFAULT NULL,
+) AUTO_INCREMENT=10;
+
+ */
+
+import java.sql.Timestamp;
+
+import org.kar.archidata.annotation.SQLAutoIncrement;
+import org.kar.archidata.annotation.SQLComment;
+import org.kar.archidata.annotation.SQLDefault;
+import org.kar.archidata.annotation.SQLIfNotExists;
+import org.kar.archidata.annotation.SQLLimitSize;
+import org.kar.archidata.annotation.SQLNotNull;
+import org.kar.archidata.annotation.SQLPrimaryKey;
+import org.kar.archidata.annotation.SQLTableName;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+@SQLTableName ("user")
+@SQLIfNotExists
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+public class User {
+ @SQLAutoIncrement // Add AUTO_INCREMENT modifier
+ @SQLPrimaryKey // Create a PRIMARY KEY based on this field
+ @SQLNotNull
+ @SQLComment("Primary key of the base")
+ public Long id = null;
+ @SQLLimitSize(256)
+ public String login = null;
+
+ public Timestamp lastConnection = null;
+ @SQLDefault("'0'")
+ @SQLNotNull
+ public boolean admin = false;
+ @SQLDefault("'0'")
+ @SQLNotNull
+ public boolean blocked = false;
+ @SQLDefault("'0'")
+ @SQLNotNull
+ public boolean removed = false;
+
+}
diff --git a/src/org/kar/archidata/model/UserExtern.java b/src/org/kar/archidata/model/UserExtern.java
new file mode 100644
index 0000000..2a1b61c
--- /dev/null
+++ b/src/org/kar/archidata/model/UserExtern.java
@@ -0,0 +1,36 @@
+package org.kar.archidata.model;
+
+/*
+CREATE TABLE `user` (
+ `id` bigint NOT NULL COMMENT 'table ID' AUTO_INCREMENT PRIMARY KEY,
+ `login` varchar(128) COLLATE 'utf8_general_ci' NOT NULL COMMENT 'login of the user',
+ `email` varchar(512) COLLATE 'utf8_general_ci' NOT NULL COMMENT 'email of the user',
+ `lastConnection` datetime NOT NULL COMMENT 'last connection time',
+ `admin` enum("TRUE", "FALSE") NOT NULL DEFAULT 'FALSE',
+ `blocked` enum("TRUE", "FALSE") NOT NULL DEFAULT 'FALSE',
+ `removed` enum("TRUE", "FALSE") NOT NULL DEFAULT 'FALSE'
+) AUTO_INCREMENT=10;
+
+ */
+
+
+public class UserExtern {
+ public Long id;
+ public String login;
+ public boolean admin;
+
+ public UserExtern(User other) {
+ this.id = other.id;
+ this.login = other.login;
+ this.admin = other.admin;
+ }
+
+ @Override
+ public String toString() {
+ return "User{" +
+ "id=" + id +
+ ", login='" + login + '\'' +
+ ", admin=" + admin +
+ '}';
+ }
+}
diff --git a/src/org/kar/archidata/model/UserPerso.java b/src/org/kar/archidata/model/UserPerso.java
new file mode 100644
index 0000000..8c1be5b
--- /dev/null
+++ b/src/org/kar/archidata/model/UserPerso.java
@@ -0,0 +1,42 @@
+package org.kar.archidata.model;
+
+/*
+CREATE TABLE `user` (
+ `id` bigint NOT NULL COMMENT 'table ID' AUTO_INCREMENT PRIMARY KEY,
+ `login` varchar(128) COLLATE 'utf8_general_ci' NOT NULL COMMENT 'login of the user',
+ `email` varchar(512) COLLATE 'utf8_general_ci' NOT NULL COMMENT 'email of the user',
+ `lastConnection` datetime NOT NULL COMMENT 'last connection time',
+ `admin` enum("TRUE", "FALSE") NOT NULL DEFAULT 'FALSE',
+ `blocked` enum("TRUE", "FALSE") NOT NULL DEFAULT 'FALSE',
+ `removed` enum("TRUE", "FALSE") NOT NULL DEFAULT 'FALSE'
+) AUTO_INCREMENT=10;
+
+ */
+
+
+public class UserPerso {
+ public Long id;
+ public String login;
+ public boolean admin;
+ public boolean blocked;
+ public boolean removed;
+
+ public UserPerso(User other) {
+ this.id = other.id;
+ this.login = other.login;
+ this.admin = other.admin;
+ this.blocked = other.blocked;
+ this.removed = other.removed;
+ }
+
+ @Override
+ public String toString() {
+ return "User{" +
+ "id=" + id +
+ ", login='" + login + '\'' +
+ ", admin=" + admin +
+ ", blocked=" + blocked +
+ ", removed=" + removed +
+ '}';
+ }
+}
diff --git a/src/org/kar/archidata/model/UserSmall.java b/src/org/kar/archidata/model/UserSmall.java
new file mode 100644
index 0000000..91cb8e8
--- /dev/null
+++ b/src/org/kar/archidata/model/UserSmall.java
@@ -0,0 +1,73 @@
+package org.kar.archidata.model;
+
+/*
+CREATE TABLE `user` (
+ `id` bigint NOT NULL COMMENT 'table ID' AUTO_INCREMENT PRIMARY KEY,
+ `login` varchar(128) COLLATE 'utf8_general_ci' NOT NULL COMMENT 'login of the user',
+ `password` varchar(128) COLLATE 'latin1_bin' NOT NULL COMMENT 'password of the user hashed (sha512)',
+ `email` varchar(512) COLLATE 'utf8_general_ci' NOT NULL COMMENT 'email of the user',
+ `emailValidate` bigint COMMENT 'date of the email validation',
+ `newEmail` varchar(512) COLLATE 'utf8_general_ci' COMMENT 'email of the user if he want to change',
+ `authorisationLevel` enum("REMOVED", "USER", "ADMIN") NOT NULL COMMENT 'user level of authorization'
+) AUTO_INCREMENT=10;
+
+ */
+
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class UserSmall {
+ public long id;
+ public String login;
+ public String email;
+ public State authorisationLevel;
+
+ public UserSmall() {
+ }
+
+ public UserSmall(long id, String login, String email, State authorisationLevel) {
+ this.id = id;
+ this.login = login;
+ this.email = email;
+ this.authorisationLevel = authorisationLevel;
+ }
+
+ public UserSmall(ResultSet rs) {
+ int iii = 1;
+ try {
+ this.id = rs.getLong(iii++);
+ this.login = rs.getString(iii++);
+ this.email = rs.getString(iii++);
+ this.authorisationLevel = State.valueOf(rs.getString(iii++));
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ }
+ /*
+ public void serialize(ResultSet rs) {
+ int iii = 1;
+ try {
+ this.id = rs.getLong(iii++);
+ this.login = rs.getString(iii++);
+ this.password = rs.getString(iii++);
+ this.email = rs.getString(iii++);
+ this.emailValidate = rs.getLong(iii++);
+ this.newEmail = rs.getString(iii++);
+ this.authorisationLevel = State.valueOf(rs.getString(iii++));
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ }
+ */
+
+ @Override
+ public String toString() {
+ return "UserSmall{" +
+ "id='" + id + '\'' +
+ ", login='" + login + '\'' +
+ ", email='" + email + '\'' +
+ ", authorisationLevel=" + authorisationLevel +
+ '}';
+ }
+}
diff --git a/src/org/kar/archidata/util/ConfigBaseVariable.java b/src/org/kar/archidata/util/ConfigBaseVariable.java
new file mode 100644
index 0000000..c0ad146
--- /dev/null
+++ b/src/org/kar/archidata/util/ConfigBaseVariable.java
@@ -0,0 +1,78 @@
+package org.kar.archidata.util;
+
+public class ConfigBaseVariable {
+
+ public static String getTmpDataFolder() {
+ String out = System.getenv("DATA_TMP_FOLDER");
+ if (out == null) {
+ return "/application/data/tmp";
+ }
+ return out;
+ }
+
+ public static String getMediaDataFolder() {
+ String out = System.getenv("DATA_FOLDER");
+ if (out == null) {
+ return "/application/data/media";
+ }
+ return out;
+ }
+
+ public static String getDBHost() {
+ String out = System.getenv("DB_HOST");
+ if (out == null) {
+ return "localhost";
+ }
+ return out;
+ }
+
+ public static String getDBPort() {
+ String out = System.getenv("DB_PORT");
+ if (out == null) {
+ return "80";
+ //return "17036";
+ }
+ return out;
+ }
+
+ public static String getDBLogin() {
+ String out = System.getenv("DB_USER");
+ if (out == null) {
+ return "root";
+ }
+ return out;
+ }
+
+ public static String getDBPassword() {
+ String out = System.getenv("DB_PASSWORD");
+ if (out == null) {
+ return "archidata_password";
+ }
+ return out;
+ }
+
+ public static String getDBName() {
+ String out = System.getenv("DB_DATABASE");
+ if (out == null) {
+ return "unknown";
+ }
+ return out;
+ }
+
+ public static String getlocalAddress() {
+ String out = System.getenv("API_ADDRESS");
+ if (out == null) {
+ return "http://0.0.0.0:80/api/";
+ }
+ return out;
+ }
+
+ public static String getSSOAddress() {
+ String out = System.getenv("SSO_ADDRESS");
+ if (out == null) {
+ return "http://sso_host/karauth/api/";
+ //return "http://192.168.1.156/karauth/api/";
+ }
+ return out;
+ }
+}
diff --git a/src/org/kar/archidata/util/DataTools.java b/src/org/kar/archidata/util/DataTools.java
new file mode 100644
index 0000000..431fc9a
--- /dev/null
+++ b/src/org/kar/archidata/util/DataTools.java
@@ -0,0 +1,346 @@
+package org.kar.archidata.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.kar.archidata.GlobalConfiguration;
+import org.kar.archidata.SqlWrapper;
+import org.kar.archidata.model.Data;
+import org.kar.archidata.db.DBEntry;
+
+public class DataTools {
+
+ public final static int CHUNK_SIZE = 1024 * 1024; // 1MB chunks
+ public final static int CHUNK_SIZE_IN = 50 * 1024 * 1024; // 1MB chunks
+ /**
+ * Upload some data
+ */
+ private static long tmpFolderId = 1;
+
+ public static void createFolder(String path) throws IOException {
+ if (!Files.exists(java.nio.file.Path.of(path))) {
+ System.out.println("Create folder: " + path);
+ Files.createDirectories(java.nio.file.Path.of(path));
+ }
+ }
+
+ public static long getTmpDataId() {
+ return tmpFolderId++;
+ }
+ public static String getTmpFileInData(long tmpFolderId) {
+ String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId;
+ try {
+ createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return filePath;
+ }
+
+ public static String getTmpFolder() {
+ String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId++;
+ try {
+ createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return filePath;
+ }
+
+ public static String getFileData(long tmpFolderId) {
+ String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator + "data";
+ try {
+ createFolder(ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return filePath;
+ }
+
+ public static Data getWithSha512(String sha512) {
+ System.out.println("find sha512 = " + sha512);
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ String query = "SELECT `id`, `deleted`, `sha512`, `mime_type`, `size` FROM `data` WHERE `sha512` = ?";
+ try {
+ PreparedStatement ps = entry.connection.prepareStatement(query);
+ ps.setString(1, sha512);
+ ResultSet rs = ps.executeQuery();
+ if (rs.next()) {
+ Data out = new Data(rs);
+ entry.disconnect();
+ return out;
+ }
+ } catch (SQLException throwables) {
+ throwables.printStackTrace();
+ }
+ entry.disconnect();
+ return null;
+
+ }
+
+ public static Data getWithId(long id) {
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ String query = "SELECT `id`, `deleted`, `sha512`, `mime_type`, `size` FROM `data` WHERE `deleted` = false AND `id` = ?";
+ try {
+ PreparedStatement ps = entry.connection.prepareStatement(query);
+ ps.setLong(1, id);
+ ResultSet rs = ps.executeQuery();
+ if (rs.next()) {
+ Data out = new Data(rs);
+ entry.disconnect();
+ return out;
+ }
+ } catch (SQLException throwables) {
+ throwables.printStackTrace();
+ }
+ entry.disconnect();
+ return null;
+ }
+
+ public static Data createNewData(long tmpUID, String originalFileName, String sha512) throws IOException, SQLException {
+ // determine mime type:
+ String mimeType = "";
+ String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1);
+ switch (extension.toLowerCase()) {
+ case "jpg":
+ case "jpeg":
+ mimeType = "image/jpeg";
+ break;
+ case "png":
+ mimeType = "image/png";
+ break;
+ case "webp":
+ mimeType = "image/webp";
+ break;
+ case "mka":
+ mimeType = "audio/x-matroska";
+ break;
+ case "mkv":
+ mimeType = "video/x-matroska";
+ break;
+ case "webm":
+ mimeType = "video/webm";
+ break;
+ default:
+ throw new IOException("Can not find the mime type of data input: '" + extension + "'");
+ }
+ String tmpPath = getTmpFileInData(tmpUID);
+ long fileSize = Files.size(Paths.get(tmpPath));
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ long uniqueSQLID = -1;
+ try {
+ // prepare the request:
+ String query = "INSERT INTO `data` (`sha512`, `mime_type`, `size`, `original_name`) VALUES (?, ?, ?, ?)";
+ PreparedStatement ps = entry.connection.prepareStatement(query,
+ Statement.RETURN_GENERATED_KEYS);
+ int iii = 1;
+ ps.setString(iii++, sha512);
+ ps.setString(iii++, mimeType);
+ ps.setLong(iii++, fileSize);
+ ps.setString(iii++, originalFileName);
+ // execute the request
+ int affectedRows = ps.executeUpdate();
+ if (affectedRows == 0) {
+ throw new SQLException("Creating data failed, no rows affected.");
+ }
+ // retreive uid inserted
+ try (ResultSet generatedKeys = ps.getGeneratedKeys()) {
+ if (generatedKeys.next()) {
+ uniqueSQLID = generatedKeys.getLong(1);
+ } else {
+ throw new SQLException("Creating user failed, no ID obtained (1).");
+ }
+ } catch (Exception ex) {
+ System.out.println("Can not get the UID key inserted ... ");
+ ex.printStackTrace();
+ throw new SQLException("Creating user failed, no ID obtained (2).");
+ }
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ entry.disconnect();
+ System.out.println("Add Data raw done. uid data=" + uniqueSQLID);
+ Data out = getWithId(uniqueSQLID);
+
+ String mediaPath = getFileData(out.id);
+ System.out.println("src = " + tmpPath);
+ System.out.println("dst = " + mediaPath);
+ Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE);
+
+ System.out.println("Move done");
+ // all is done the file is corectly installed...
+
+ return out;
+ }
+
+ public static void undelete(Long id) {
+ DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
+ String query = "UPDATE `data` SET `deleted` = false WHERE `id` = ?";
+ try {
+ PreparedStatement ps = entry.connection.prepareStatement(query);
+ ps.setLong(1, id);
+ ps.execute();
+ } catch (SQLException throwables) {
+ throwables.printStackTrace();
+ }
+ entry.disconnect();
+ }
+
+ public static String saveTemporaryFile(InputStream uploadedInputStream, long idData) {
+ return saveFile(uploadedInputStream, getTmpFileInData(idData));
+ }
+
+ public static void removeTemporaryFile(long idData) {
+ String filepath = getTmpFileInData(idData);
+ if (Files.exists(Paths.get(filepath))) {
+ try {
+ Files.delete(Paths.get(filepath));
+ } catch (IOException e) {
+ System.out.println("can not delete temporary file : " + Paths.get(filepath));
+ e.printStackTrace();
+ }
+ }
+ }
+
+ // save uploaded file to a defined location on the server
+ public static String saveFile(InputStream uploadedInputStream, String serverLocation) {
+ String out = "";
+ try {
+ OutputStream outpuStream = new FileOutputStream(new File(
+ serverLocation));
+ int read = 0;
+ byte[] bytes = new byte[CHUNK_SIZE_IN];
+ MessageDigest md = MessageDigest.getInstance("SHA-512");
+
+ outpuStream = new FileOutputStream(new File(serverLocation));
+ while ((read = uploadedInputStream.read(bytes)) != -1) {
+ //System.out.println("write " + read);
+ md.update(bytes, 0, read);
+ outpuStream.write(bytes, 0, read);
+ }
+ System.out.println("Flush input stream ... " + serverLocation);
+ System.out.flush();
+ outpuStream.flush();
+ outpuStream.close();
+ // create the end of sha512
+ byte[] sha512Digest = md.digest();
+ // convert in hexadecimal
+ out = bytesToHex(sha512Digest);
+ uploadedInputStream.close();
+ } catch (IOException ex) {
+ System.out.println("Can not write in temporary file ... ");
+ ex.printStackTrace();
+ } catch (NoSuchAlgorithmException ex) {
+ System.out.println("Can not find sha512 algorithms");
+ ex.printStackTrace();
+ }
+ return out;
+ }
+
+ // curl http://localhost:9993/api/users/3
+ //@Secured
+ /*
+ @GET
+ @Path("{id}")
+ //@RolesAllowed("GUEST")
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ public Response retriveData(@HeaderParam("Range") String range, @PathParam("id") Long id) throws Exception {
+ return retriveDataFull(range, id, "no-name");
+ }
+ */
+
+ public static String bytesToHex(byte[] bytes) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append(String.format("%02x", b));
+ }
+ return sb.toString();
+ }
+
+ public static String multipartCorrection(String data) {
+ if (data == null) {
+ return null;
+ }
+ if (data.isEmpty()) {
+ return null;
+ }
+ if (data.contentEquals("null")) {
+ return null;
+ }
+ return data;
+ }
+
+ public static Response uploadCover(Class clazz,
+ Long id,
+ String fileName,
+ InputStream fileInputStream,
+ FormDataContentDisposition fileMetaData
+ ) {
+ try {
+ // correct input string stream :
+ fileName = multipartCorrection(fileName);
+
+ //public NodeSmall uploadFile(final FormDataMultiPart form) {
+ System.out.println("Upload media file: " + fileMetaData);
+ System.out.println(" - id: " + id);
+ System.out.println(" - file_name: " + fileName);
+ System.out.println(" - fileInputStream: " + fileInputStream);
+ System.out.println(" - fileMetaData: " + fileMetaData);
+ System.out.flush();
+ T media = SqlWrapper.get(clazz, id);
+ if (media == null) {
+ return Response.notModified("Media Id does not exist or removed...").build();
+ }
+
+ long tmpUID = getTmpDataId();
+ String sha512 = saveTemporaryFile(fileInputStream, tmpUID);
+ Data data = getWithSha512(sha512);
+ if (data == null) {
+ System.out.println("Need to add the data in the BDD ... ");
+ System.out.flush();
+ try {
+ data = createNewData(tmpUID, fileName, sha512);
+ } catch (IOException ex) {
+ removeTemporaryFile(tmpUID);
+ ex.printStackTrace();
+ return Response.notModified("can not create input media").build();
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ removeTemporaryFile(tmpUID);
+ return Response.notModified("Error in SQL insertion ...").build();
+ }
+ } else if (data.deleted == true) {
+ System.out.println("Data already exist but deleted");
+ System.out.flush();
+ undelete(data.id);
+ data.deleted = false;
+ } else {
+ System.out.println("Data already exist ... all good");
+ System.out.flush();
+ }
+ // Fist step: retrieve all the Id of each parents:...
+ System.out.println("Find typeNode");
+ SqlWrapper.addLink(clazz, id, "cover", data.id);
+ return Response.ok(SqlWrapper.get(clazz, id)).build();
+ } catch (Exception ex) {
+ System.out.println("Cat ann unexpected error ... ");
+ ex.printStackTrace();
+ }
+ return Response.serverError().build();
+ }
+}
diff --git a/src/org/kar/archidata/util/JWTWrapper.java b/src/org/kar/archidata/util/JWTWrapper.java
new file mode 100644
index 0000000..85aeac0
--- /dev/null
+++ b/src/org/kar/archidata/util/JWTWrapper.java
@@ -0,0 +1,175 @@
+package org.kar.archidata.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.UUID;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JOSEObjectType;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSSigner;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+
+public class JWTWrapper {
+ private static RSAKey rsaJWK = null;;
+ private static RSAKey rsaPublicJWK = null;
+
+ public static class PublicKey {
+ public String key;
+
+ public PublicKey(String key) {
+ this.key = key;
+ }
+ public PublicKey() {
+ }
+ }
+ public static void initLocalTokenRemote(String ssoUri, String application) throws IOException, ParseException {
+ // check Token:
+ URL obj = new URL(ssoUri + "public_key");
+ System.out.println("Request token from:" + obj);
+ HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+ con.setRequestMethod("GET");
+ con.setRequestProperty("User-Agent", application);
+ con.setRequestProperty("Cache-Control", "no-cache");
+ con.setRequestProperty("Content-Type", "application/json");
+ con.setRequestProperty("Accept", "application/json");
+ int responseCode = con.getResponseCode();
+
+ System.out.println("GET Response Code :: " + responseCode);
+ if (responseCode == HttpURLConnection.HTTP_OK) { // success
+ BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+
+ String inputLine;
+ StringBuffer response = new StringBuffer();
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ in.close();
+ // print result
+ //System.out.println(response.toString());
+ ObjectMapper mapper = new ObjectMapper();
+ PublicKey values = mapper.readValue(response.toString(), PublicKey.class);
+ rsaPublicJWK = RSAKey.parse(values.key);
+ return;
+ }
+ System.out.println("GET JWT validator token not worked");
+ }
+
+ public static void initLocalToken() throws Exception{
+ // RSA signatures require a public and private RSA key pair, the public key
+ // must be made known to the JWS recipient in order to verify the signatures
+ try {
+ String generatedStringForKey = UUID.randomUUID().toString();
+ rsaJWK = new RSAKeyGenerator(2048).keyID(generatedStringForKey).generate();
+ rsaPublicJWK = rsaJWK.toPublicJWK();
+ //System.out.println("RSA key (all): " + rsaJWK.toJSONString());
+ //System.out.println("RSA key (pub): " + rsaPublicJWK.toJSONString());
+ } catch (JOSEException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ System.out.println("Can not generate teh public abnd private keys ...");
+ rsaJWK = null;
+ rsaPublicJWK = null;
+ }
+ }
+
+ public static void initValidateToken(String publicKey) {
+ try {
+ rsaPublicJWK = RSAKey.parse(publicKey);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ System.out.println("Can not retrieve public Key !!!!!!!! RSAKey='" + publicKey + "'");
+ }
+
+ }
+ public static String getPublicKey() {
+ if (rsaPublicJWK == null) {
+ return null;
+ }
+ return rsaPublicJWK.toJSONString();
+ }
+
+ /**
+ * Create a token with the provided elements
+ * @param userID UniqueId of the USER (global unique ID)
+ * @param userLogin Login of the user (never change)
+ * @param isuer The one who provide the Token
+ * @param timeOutInMunites Expiration of the token.
+ * @return the encoded token
+ */
+ public static String generateJWToken(long userID, String userLogin, String isuer, int timeOutInMunites) {
+ if (rsaJWK == null) {
+ System.out.println("JWT private key is not present !!!");
+ return null;
+ }
+ try {
+ // Create RSA-signer with the private key
+ JWSSigner signer = new RSASSASigner(rsaJWK);
+ // Prepare JWT with claims set
+ JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+ .subject(Long.toString(userID))
+ .claim("login", userLogin)
+ .issuer(isuer)
+ .issueTime(new Date())
+ .expirationTime(new Date(new Date().getTime() + 60 * timeOutInMunites * 1000 /* millisecond */))
+ .build();
+
+ SignedJWT signedJWT = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT)/*.keyID(rsaJWK.getKeyID())*/.build(), claimsSet);
+
+ // Compute the RSA signature
+ signedJWT.sign(signer);
+ // serialize the output...
+ return signedJWT.serialize();
+ } catch (JOSEException ex) {
+ ex.printStackTrace();
+ }
+ return null;
+ }
+
+ public static JWTClaimsSet validateToken(String signedToken, String isuer) {
+ if (rsaPublicJWK == null) {
+ System.out.println("JWT public key is not present !!!");
+ return null;
+ }
+ try {
+ // On the consumer side, parse the JWS and verify its RSA signature
+ SignedJWT signedJWT = SignedJWT.parse(signedToken);
+
+ JWSVerifier verifier = new RSASSAVerifier(rsaPublicJWK);
+ if (!signedJWT.verify(verifier)) {
+ System.out.println("JWT token is NOT verified ");
+ return null;
+ }
+ if (!new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime())) {
+ System.out.println("JWT token is expired now = " + new Date() + " with=" + signedJWT.getJWTClaimsSet().getExpirationTime() );
+ return null;
+ }
+ if (!isuer.equals(signedJWT.getJWTClaimsSet().getIssuer())) {
+ System.out.println("JWT issuer is wong: '" + isuer + "' != '" + signedJWT.getJWTClaimsSet().getIssuer() + "'" );
+ return null;
+ }
+ // the element must be validated outside ...
+ //System.out.println("JWT token is verified 'alice' =?= '" + signedJWT.getJWTClaimsSet().getSubject() + "'");
+ //System.out.println("JWT token isuer 'https://c2id.com' =?= '" + signedJWT.getJWTClaimsSet().getIssuer() + "'");
+ return signedJWT.getJWTClaimsSet();
+ } catch (JOSEException ex) {
+ ex.printStackTrace();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/src/org/kar/archidata/util/PublicKey.java b/src/org/kar/archidata/util/PublicKey.java
new file mode 100644
index 0000000..f6968be
--- /dev/null
+++ b/src/org/kar/archidata/util/PublicKey.java
@@ -0,0 +1,10 @@
+package org.kar.archidata.util;
+
+
+public class PublicKey {
+ public String key;
+
+ public PublicKey(String key) {
+ this.key = key;
+ }
+}
\ No newline at end of file