diff --git a/NiHao/.classpath b/NiHao/.classpath new file mode 100644 index 0000000..aa3f3f9 --- /dev/null +++ b/NiHao/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/NiHao/.externalToolBuilders/NiHao BUILD.launch b/NiHao/.externalToolBuilders/NiHao BUILD.launch new file mode 100644 index 0000000..d7cbf54 --- /dev/null +++ b/NiHao/.externalToolBuilders/NiHao BUILD.launch @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/NiHao/.project b/NiHao/.project new file mode 100644 index 0000000..ccccc3b --- /dev/null +++ b/NiHao/.project @@ -0,0 +1,27 @@ + + + NiHao + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.ui.externaltools.ExternalToolBuilder + full,incremental, + + + LaunchConfigHandle + <project>/.externalToolBuilders/NiHao BUILD.launch + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/NiHao/.settings/org.eclipse.jdt.core.prefs b/NiHao/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..bb35fa0 --- /dev/null +++ b/NiHao/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/NiHao/Build.xml b/NiHao/Build.xml new file mode 100644 index 0000000..2c9db55 --- /dev/null +++ b/NiHao/Build.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NiHao/LICENSE b/NiHao/LICENSE new file mode 100644 index 0000000..3260e4f --- /dev/null +++ b/NiHao/LICENSE @@ -0,0 +1,204 @@ +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation + distributed under this Agreement, and +b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are + distributed by that particular Contributor. A Contribution 'originates' + from a Contributor if it was added to the Program by such Contributor + itself or anyone acting on such Contributor's behalf. Contributions do not + include additions to the Program which: (i) are separate modules of + software distributed in conjunction with the Program under their own + license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + a) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free copyright license to + reproduce, prepare derivative works of, publicly display, publicly + perform, distribute and sublicense the Contribution of such Contributor, + if any, and such derivative works, in source code and object code form. + b) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free patent license under + Licensed Patents to make, use, sell, offer to sell, import and otherwise + transfer the Contribution of such Contributor, if any, in source code and + object code form. This patent license shall apply to the combination of + the Contribution and the Program if, at the time the Contribution is + added by the Contributor, such addition of the Contribution causes such + combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Contribution. + No hardware per se is licensed hereunder. + c) Recipient understands that although each Contributor grants the licenses + to its Contributions set forth herein, no assurances are provided by any + Contributor that the Program does not infringe the patent or other + intellectual property rights of any other entity. Each Contributor + disclaims any liability to Recipient for claims brought by any other + entity based on infringement of intellectual property rights or + otherwise. As a condition to exercising the rights and licenses granted + hereunder, each Recipient hereby assumes sole responsibility to secure + any other intellectual property rights needed, if any. For example, if a + third party patent license is required to allow Recipient to distribute + the Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + d) Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under +its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + b) its license agreement: + i) effectively disclaims on behalf of all Contributors all warranties + and conditions, express and implied, including warranties or + conditions of title and non-infringement, and implied warranties or + conditions of merchantability and fitness for a particular purpose; + ii) effectively excludes on behalf of all Contributors all liability for + damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; + iii) states that any provisions which differ from this Agreement are + offered by that Contributor alone and not by any other party; and + iv) states that source code for the Program is available from such + Contributor, and informs licensees how to obtain it in a reasonable + manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + b) a copy of this Agreement must be included with each copy of the Program. + Contributors may not remove or alter any copyright notices contained + within the Program. + +Each Contributor must identify itself as the originator of its Contribution, +if +any, in a manner that reasonably allows subsequent Recipients to identify the +originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a manner +which does not create potential liability for other Contributors. Therefore, +if a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, +damages and costs (collectively "Losses") arising from claims, lawsuits and +other legal actions brought by a third party against the Indemnified +Contributor to the extent caused by the acts or omissions of such Commercial +Contributor in connection with its distribution of the Program in a commercial +product offering. The obligations in this section do not apply to any claims +or Losses relating to any actual or alleged intellectual property +infringement. In order to qualify, an Indemnified Contributor must: +a) promptly notify the Commercial Contributor in writing of such claim, and +b) allow the Commercial Contributor to control, and cooperate with the +Commercial Contributor in, the defense and any related settlement +negotiations. The Indemnified Contributor may participate in any such claim at +its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If +that Commercial Contributor then makes performance claims, or offers +warranties related to Product X, those performance claims and warranties are +such Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each +Recipient is solely responsible for determining the appropriateness of using +and distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to the +risks and costs of program errors, compliance with applicable laws, damage to +or loss of data, programs or equipment, and unavailability or interruption of +operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION +LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE +EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of the +remainder of the terms of this Agreement, and without further action by the +parties hereto, such provision shall be reformed to the minimum extent +necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program itself +(excluding combinations of the Program with other software or hardware) +infringes such Recipient's patent(s), then such Recipient's rights granted +under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient's rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient's obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue +and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to +time. No one other than the Agreement Steward has the right to modify this +Agreement. The Eclipse Foundation is the initial Agreement Steward. The +Eclipse Foundation may assign the responsibility to serve as the Agreement +Steward to a suitable separate entity. Each new version of the Agreement will +be given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version of the +Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly +stated in Sections 2(a) and 2(b) above, Recipient receives no rights or +licenses to the intellectual property of any Contributor under this Agreement, +whether expressly, by implication, estoppel or otherwise. All rights in the +Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial in +any resulting litigation. + diff --git a/NiHao/bin/.gitignore b/NiHao/bin/.gitignore new file mode 100644 index 0000000..b5ca82a --- /dev/null +++ b/NiHao/bin/.gitignore @@ -0,0 +1 @@ +/nihao/ diff --git a/NiHao/doc/Info.html b/NiHao/doc/Info.html new file mode 100644 index 0000000..0ce5466 --- /dev/null +++ b/NiHao/doc/Info.html @@ -0,0 +1,356 @@ + + + NiHao Help + + + + + +
+
+
+NiHao 0.8 (C) XWolf 2012 +
+
+ NiHao es un framework de aplicaciones autocontenido, por lo que NO esta basado en ningún otro + framework ni librería más que lo que corresponde a J2EE.
+ Esta diseñado para ser amable con la memoria mientras se busca la mayor rapidez de + ejecución posible. Equilibrandolo finalmente con la sencillez de configuración y + mantenimiento. Haciendolo perfecto tanto para grándes como pequeñas aplicaciones. +

+ El diseño interno de QCore está basado en técnicas vistas en multitud de frameworks actuales, pero tiene su propia implementación, mejornado la integración y eliminando código e instancias innecesiarias. +

+ Una de esas técnicas es la capacidad de configurar el framework usando un archivo + de contexto, como hace Srping pero en vez + de usar una estructura en XML, usa un lenguaje propio, facilitando enormemente la + lectura y modificación de dicho archivo. +

+ O Ibatis, para el acceso a base de datos. + Plasmando en este mismo fichero de confugración la configuración de acceso a datos, y + las queries que atacan a esta.
+ Una peculiaridad de QCore es la posibilidad de definir el engine de base de datos + usado, y poder realizar multiples queris para los distintos engines.
+ De esa manera QCore es capaz de decidir la query a usar dependiendo del engine del + acceso a datos, y por lo tanto facilitar el cambio de engine de una base de datos a + otra sin penalización en el desarroyo. +

+
+ +
+
Instalación:
+
+ Ejecutar en una Base de datos el script correspondiente a su engine, este generará + la estructura, y datos básicos para el uso de QCore.
+ QCore busca por defecto el archivo de contexto qcore.ctx dentro de META-INF. Se publican + unos archivos .ctx de ejemplo para la configuración inicial.
+ Añadir QCore.jar como libreria de aplicación, y añadir al META-INF de la aplicación + web los archivos de ejeplo situados en doc/META-INF.
+ Editar el web.xml, añadiendo el correspondiente filtro, es recomendable usar este filtro como primer filtro +
+<filter>
+  <filter-name>SessionFilter</filter-name>
+  <filter-class>qcore.WebFilter</filter-class>
+</filter>
+
+<filter-mapping>
+  <filter-name>SessionFilter</filter-name>
+  <url-pattern>*.do</url-pattern>
+</filter-mapping>
+<filter-mapping>
+  <filter-name>SessionFilter</filter-name>
+  <url-pattern>*.jsp</url-pattern>
+</filter-mapping>
+<filter-mapping>
+  <filter-name>SessionFilter</filter-name>
+  <url-pattern>*.html</url-pattern>
+</filter-mapping>
+
+
+
+ +
+
Lenguaje de contexto:
+
+ QCore utiliza un formato propio para definir una persistencia de objetos que son instanciados como objetos para definir + distintas configuraciones y estructuras complejas. + + QCore leera por defecto el archivo localizado en NETA-INF/qcore.ctx el cual ha de definirse en el proyecto de aplicación + +
Definición de Proveedores
+
+
 Estructura:
+  provider <name> default <defaultname>{
+   <implname>
+   <implname>
+  }
+ 
+ El nombre <name> ha de coincidir con el de la clase del proveedor (sin el paquete) + El nombre <defaultname> ha de estar en la lista de <implname> + Los proveedores adicionales han de estar listados en <implname> +
+ +
Definición de Objeto
+
+
 Estructura:
+  object [<name>] class <class>{[(<parameter>[,<parameter>,...])
+   [<property>=<value>]
+   [<property>=<value>]
+   [...]
+  }
+ 
+ El nombre <name> ha de ser único entre todas las definiciones del contexto, + excepto con las definiciones de queries. La clase <class> ha de un nombre + completo de una clase instanciable. El parametro <parameter> son los parametros + del constructor. Tanto <property> como <value> se usan para asignar + propiedades de la clase mediante su seter o directamente en la propiedad privada. +
+ +
Definición de Array
+
+
 Estructura:
+  array [<name>] of <class> {
+   [<value>]
+   [<value>]
+   [...]
+  }
+ 
+ El nombre <name> ha de ser único entre todas las definiciones del contexto, excepto con las definiciones de queries + El valor <value> se usa para definir los vlares del array +
+ +
Definición de Lista
+
+
 Estructura:
+  list [<name>]{
+   [<value>]
+   [<value>]
+   [...]
+  }
+ 
+ El nombre <name> ha de ser único entre todas las definiciones del contexto, excepto con las definiciones de queries + El valor <value> se usa para definir los vlares del array +
+ +
Definición de HashMap
+
+
 Estructura:
+  map [<name>] {
+   [{<key>,<value>}]
+   [{<key>,<value>}]
+   [...]
+  }
+ 
+ El nombre <name> ha de ser único entre todas las definiciones del contexto, excepto con las definiciones de queries + La clave <key> es la clave del HashMap + El valor <value> es el valor para la clave <key> +
+ +
Definición de null
+
+
 Estructura:
+  null
+ 
+ Es la definicion del valor null +
+ +
Definición de literal String
+
+
 Estructura:
+  "<value>"
+ 
+ El valor <value> es el contenido del String sin cambios. +
+ +
Definición de literal numerica
+
+
 Estructura:
+  <value>
+ 
+ El valor <value> es un número, usando el punto "." para decimales +
+ +
Definición de literal booleana
+
+
 Estructura:
+  true|false
+ 
+ Literal sin commillas, true para verdadero y false para falso. +
+ +
Definición de Queries
+
+
 Estructura:
+  query <name> [engine <enginename>|any] [type <type>] { <querystr> }
+ 
+ El engine ha de coincidir con el especificado en el acceso a datos (case sensitive) any para cualquera, + si no se especifica engine, se asume any.
+ Querystr puede contener información adicional usando la almoadilla como caracter de escape: +
    +
  • ## Hace referencia a un tipo escalar unico
  • +
  • #[<numero indice>]# Hace referencia a un elemento de un array Ex: #1#,#2#,#10#
  • +
  • #<nombre miembro># Hace referencia a una propiedad de un objeto, o a una clave en un hashmap Ex: #userName#,#userPwd#
  • +
+</div> +</div> +
+ +
+
+ +
+
Conocimientos básicos:
+
+ La base de datos por defecto de QCore contiene 2 grupos: Admin y Guest. Y un + usuario: admin (contraseña 1234), perteneciente al grupo Admin. +

+ El grupo guest es obigatorio, ya que es el grupo que se le asigna a los usuarios no logados. + es recomendable que no tenga permisos, pero que sea un gupo activo. +

+ El fichero de contexto ha de tener de forma obligatoria un objeto llamado QCoreConf. Ejemplo:
+

+object QCoreConf class nihao.QCoreConf{
+ loginConf=object class nihao.login.LoginConf{
+  modules=array of nihao.login.ILoginModule{
+   //object class nihao.login.LoginModuleSSL{}
+   object class nihao.login.LoginModuleRequest{}
+  };
+  pageLogin="login.jsp";
+  pageEnd=  "end.jsp";
+  pageError="error.jsp";"
+  pageForbidden="forbidden.jsp";
+  excluded=array of String{
+  };
+ }
+ worksetCacheSize=10;
+}
+ 
+
+Uso en JSP:
+ Se usa a través del taglib de QCore, defeinir en cada página:
+  <%@ taglib uri="http://www.xwolf.es/qcore" prefix="Q"%>
+  para usar el taglib
+
+Id en tags
+ Para que el tag coja datos del Workset se vinculan mediante el mismo nombre de campo del workset que el id del tag.
+ En este caso el tag es de ida y vuelta (el valor se envia a la página, pero este también se notifica al servidor).
+ Existen casos especiales, para obtener valores, pero estos no serán enviados al servidor de manera natural.
+ Estos casos son:
+  · Nombres que empiezan por "|": Obtienen el valor de los atributos del PageContext, el nombre en el PageContext incluye
+  el simbolo "|"
+  · Nombres que empiezan por "@": Obtienen el valor del archivo de contexto de aplicación, sin incluir el simbolo inicial. 
+  · Nombres que empiezan por "$": Obtienen el valor de la sesión, sin incluir el simbolo inicial. 
+  · Nombres que empiezan por "%": Obtienen el valor de el parametro del request, sin incluir el simbolo inicial.
+  · Puntos en el nombre: Si los nombres de campo estan separados por ".", se realiza una navegación. por
+  los elementos de los objetos, pudiendo emplear cualquier profundidad. Si algún elemento en la ruta de
+  navegación, es null, se retorna null.
+ Para enviar al servidor hay que especificar el attributo send="<nombre campo>". Dónde
+  <nombre campo> es el nombre del campo del workset.
+ 
+
+Proveedores de acceso a datos:
+ Estructura:
+  object MainDatabaseConnection class xwolf.qcore.db.DataSourceProviderJNDI{("java:comp/env/jdbc/<name>")
+   engine="<engine>" //opcional
+  }
+  object MainDatabaseConnection class xwolf.qcore.db.DataSourceProviderJDBC{("jdbc:<engine>://<host>:3306/<ddbb>")
+   driver="<driver to initialize>" // opcional
+   engine="<engine>" //opcional
+   user="<user>"
+   password="<password>"
+  }
+
+
+
+ +
+ + \ No newline at end of file diff --git a/NiHao/doc/META-INF/nihao.ctx b/NiHao/doc/META-INF/nihao.ctx new file mode 100644 index 0000000..e954190 --- /dev/null +++ b/NiHao/doc/META-INF/nihao.ctx @@ -0,0 +1,41 @@ +provider DataSourceProvider default MainDatabaseConnection{ + MainDatabaseConnection +// AuxiliarDatabaseConnection +} + +provider LogProvider default ConsoleLog{ + ConsoleLog +} + +object NiHaoConf class nihao.NiHaoConf{ + loginConf=object class nihao.login.LoginConf{ + modules=array of nihao.login.ILoginModule{ + //object class nihao.login.LoginModuleSSL{} + object class nihao.login.LoginModuleRequest{} + }; + pageLogin="login.jsp"; + pageEnd= "end.jsp"; + pageError="error.jsp"; + pageForbidden="forbidden.jsp"; + excluded=array of String{ + }; + }; + worksetCacheSize=10; +} + + +object MainDatabaseConnection class nihao.db.DataSourceProviderJDBC{("jdbc:mysql://localhost:3306/qcore") + driver="com.mysql.jdbc.Driver"; + engine="mysql"; + user="root"; + password="toor"; +} + +object ConsoleLog class nihao.log.ConsoleLogProvider{//(map{ +// ["xwolf.qcore","warn"] +// ["xwolf.qcore.db","all"] +// }) +} + +include "query - nihao.ctx"; +include "pages.ctx"; \ No newline at end of file diff --git a/NiHao/doc/META-INF/pages.ctx b/NiHao/doc/META-INF/pages.ctx new file mode 100644 index 0000000..5c00f69 --- /dev/null +++ b/NiHao/doc/META-INF/pages.ctx @@ -0,0 +1,6 @@ +page "/index.jsp" public; +page "/hello.jsp" private workset testws; +page "/test.jsp" for test,test2 workset test; +page "/noadmin.jsp" for test,test2,!admin workset test; //! deny certain group +page "/test_result.jsp" for test,test2 workset test; +page ~"/adm/(.+)" for admin workset admin; diff --git a/NiHao/doc/META-INF/query - nihao.ctx b/NiHao/doc/META-INF/query - nihao.ctx new file mode 100644 index 0000000..a25341d --- /dev/null +++ b/NiHao/doc/META-INF/query - nihao.ctx @@ -0,0 +1,89 @@ +// ===== NiHao Framework Default database context. +// ===== This file contains all the minimum database action needed for NiHao framework. +// ===== All this queries are generic SQL, but with MySQL specific features for table creations. +// ===== You can freely modify this file to make it compatible for your specific database engine. + +// ===== CHANGESETS +changeset "BOOTSTRAP" engine any author "NiHao"{ + CREATE TABLE nhao_changesets ( + id VARCHAR(128) NULL, + author VARCHAR(64) NULL, + executed TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, + hash CHAR(64) NULL + ) +} + +changeset "BOOTSTRAP" engine mysql author "NiHao"{ + CREATE TABLE `nhao_changesets` ( + `id` VARCHAR(128) NULL, + `author` VARCHAR(64) NULL, + `executed` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, + `hash` CHAR(64) NULL + ) CHARSET=utf8 COLLATE='utf8_general_ci' ENGINE=InnoDB +} + +query nhaochse_getChange engine any type nihao.dto.ChangesetDTO{ + SELECT * from nhao_changesets where id=## +} + +query nhaochse_insertChange engine any type nihao.dto.ChangesetDTO{ + INSERT INTO nhao_changesets (id,author,hash) values (#id#,#author#,#hash#) +} + +query nhaochse_updateChange engine any{ + UPDATE nhao_changesets set executed=CURRENT_TIMESTAMP +} + +// ===== LOGIN AND AUTHORIZATION +changeset "HiHao:create:UsersTable" engine mysql author "NiHao"{ + CREATE TABLE nhao_users ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT, + nick VARCHAR(67) NOT NULL, + pwd CHAR(40) NOT NULL, + name VARCHAR(250) NOT NULL, + since TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + lastaccess TIMESTAMP NULL, + active INT(1) NOT NULL DEFAULT '1', + PRIMARY KEY (id) + ) COLLATE=utf8_general_ci ENGINE=InnoDB +} +changeset "HiHao:create:GroupsTable" engine mysql author "NiHao"{ + CREATE TABLE nhao_groups ( + id INT UNSIGNED NOT NULL, + name VARCHAR(62) NOT NULL, + active INT(1) NOT NULL DEFAULT '1', + PRIMARY KEY (id) + ) COLLATE=utf8_general_ci ENGINE=InnoDB +} +changeset "HiHao:create:relUGTable" engine mysql author "NiHao"{ + CREATE TABLE nhao_rel_usergroups ( + userid INT UNSIGNED NOT NULL REFERENCES nhao_users(id), + groupid INT UNSIGNED NOT NULL REFERENCES nhao_groups(id), + PRIMARY KEY (userid, groupid) + ) ENGINE=InnoDB; +} +changeset "HiHao:insert:basic user data" engine mysql author "NiHao"{ + insert into nhao_users (id,nick,pwd,name) values (1,'admin','7110EDA4D09E062AA5E4A390B0A572AC0D2C0220','Administrator'); + insert into nhao_groups (id,name,active) values (1,'Administrators',1); + insert into nhao_groups (id,name,active) values (2,'Users',1); + insert into nhao_groups (id,name,active) values (3,'Guest',1); + insert into nhao_rel_usergroups (userid,groupid) values (1,1); + insert into nhao_rel_usergroups (userid,groupid) values (1,2); +} + +query nhaologn_getUserByNickPwd engine any type nihao.login.beans.DBUser{ + SELECT id,nick,display FROM nhao_users WHERE nick=#nick# AND pwd=#pwd# AND active=1 +} + +query nhaologn_getUserByNick engine any type nihao.login.beans.DBUser{ + SELECT id,nick,display FROM nhao_users WHERE nick=#nick# AND active=1 +} + +query nhaologn_getGroupsForUser engine any type nihao.login.Group{ + SELECT id,name FROM nhao_groups WHERE id IN (SELECT groupid FROM nhao_rel_usergroups WHERE userid=#id#) AND active=1 +} + +query nhaologn_getGroupByName engine any type nihao.login.Group{ + SELECT id,name FROM nhao_groups WHERE name=## +} + diff --git a/NiHao/doc/TO DO.txt b/NiHao/doc/TO DO.txt new file mode 100644 index 0000000..abde0ab --- /dev/null +++ b/NiHao/doc/TO DO.txt @@ -0,0 +1,36 @@ +Acceso a datos +Login +TAGLIB + +Queryset (para los scripts tipo SQL+, pequeñas variables para diseñar pequeños procesos) + +Eliminar de la ram las instancias de queris definidas para engines no usados + (y eliminar las queries "any" si se definen las de cada engine particular disponible) + +Workset: + +Workset class + +Workflow: + +Workflow workset { + +} + +Sample: + +Workset alumn class tester.workfow.AlumnBean +// String alumno;HashMap notas;String aula; + +Workflow setnote workset alumn{ + begin: + go "\setAlumnData.jsp" for Director // for define el grupo al que se muestra el flujo + if [cancel] kill // kill, mata el flujo, reset lo resetea (reinstancia el workset y reinicia), pero sigue pendiente de hacer, drop suelta el flujo disponible por hacer, goto lo lleva a un paso en concreto + setNotes: + go "\setNotes.jsp" for Profesor where %user.aula%=#aula# // where filtra, %% define una variable de flujo ## una variable de workset + if [cancel] goto begin + if #allNotes# goto end + goto setNotes + end: + go "\muestraNota.jsp" for Alumno where %user%=#aulmno# +} diff --git a/NiHao/doc/sql/QCore MySQL.sql b/NiHao/doc/sql/QCore MySQL.sql new file mode 100644 index 0000000..fdc14d0 --- /dev/null +++ b/NiHao/doc/sql/QCore MySQL.sql @@ -0,0 +1,87 @@ +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; + +/*Table structure for table `clids` */ + +DROP TABLE IF EXISTS `clids`; + +CREATE TABLE `clids` ( + `clids` int(10) unsigned NOT NULL AUTO_INCREMENT, + `type` char(1) COLLATE utf8_spanish_ci NOT NULL DEFAULT 'I', + PRIMARY KEY (`clids`), + UNIQUE KEY `clids_UNIQUE` (`clids`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci; + +/*Data for the table `clids` */ + +/*Table structure for table `pages` */ + +DROP TABLE IF EXISTS `pages`; + +CREATE TABLE `pages` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `url` varchar(250) COLLATE utf8_spanish_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `id_UNIQUE` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci; + +/*Data for the table `pages` */ + +/*Table structure for table `qcore_groups` */ + +DROP TABLE IF EXISTS `qcore_groups`; + +CREATE TABLE `qcore_groups` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(45) COLLATE utf8_spanish_ci NOT NULL, + `active` int(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `groups_id_UNIQUE` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci; + +/*Data for the table `qcore_groups` */ + +insert into `qcore_groups`(`id`,`name`,`active`) values (1,'Guest',1); + +/*Table structure for table `qcore_rel_usergroups` */ + +DROP TABLE IF EXISTS `qcore_rel_usergroups`; + +CREATE TABLE `qcore_rel_usergroups` ( + `userid` int(10) unsigned NOT NULL, + `groupid` int(10) unsigned NOT NULL, + PRIMARY KEY (`userid`,`groupid`), + UNIQUE KEY `userid_UNIQUE` (`userid`), + UNIQUE KEY `groupid_UNIQUE` (`groupid`), + KEY `rel_usergroups_user` (`userid`), + KEY `rel_usergroups_group` (`groupid`), + CONSTRAINT `rel_usergroups_group` FOREIGN KEY (`groupid`) REFERENCES `qcore_groups` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT `rel_usergroups_user` FOREIGN KEY (`userid`) REFERENCES `qcore_users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci; + +/*Data for the table `qcore_rel_usergroups` */ + +insert into `qcore_rel_usergroups`(`userid`,`groupid`) values (1,1); + +/*Table structure for table `qcore_users` */ + +DROP TABLE IF EXISTS `qcore_users`; + +CREATE TABLE `qcore_users` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `nick` varchar(45) COLLATE utf8_spanish_ci NOT NULL, + `pwd` char(40) CHARACTER SET ascii NOT NULL, + `display` varchar(250) COLLATE utf8_spanish_ci DEFAULT NULL, + `since` datetime NOT NULL, + `lastaccess` datetime DEFAULT NULL, + `active` int(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `qcore_user_id_UNIQUE` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci; + +/*Data for the table `qcore_users` */ + +insert into `qcore_users`(`id`,`nick`,`pwd`,`display`,`since`,`lastaccess`,`active`) values (1,'admin','7110EDA4D09E062AA5E4A390B0A572AC0D2C0220','Administrator','2012-04-10 18:48:35',NULL,1); + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; diff --git a/NiHao/release/NiHao-0.8b.html b/NiHao/release/NiHao-0.8b.html new file mode 100644 index 0000000..0ce5466 --- /dev/null +++ b/NiHao/release/NiHao-0.8b.html @@ -0,0 +1,356 @@ + + + NiHao Help + + + + + +
+
+
+NiHao 0.8 (C) XWolf 2012 +
+
+ NiHao es un framework de aplicaciones autocontenido, por lo que NO esta basado en ningún otro + framework ni librería más que lo que corresponde a J2EE.
+ Esta diseñado para ser amable con la memoria mientras se busca la mayor rapidez de + ejecución posible. Equilibrandolo finalmente con la sencillez de configuración y + mantenimiento. Haciendolo perfecto tanto para grándes como pequeñas aplicaciones. +

+ El diseño interno de QCore está basado en técnicas vistas en multitud de frameworks actuales, pero tiene su propia implementación, mejornado la integración y eliminando código e instancias innecesiarias. +

+ Una de esas técnicas es la capacidad de configurar el framework usando un archivo + de contexto, como hace Srping pero en vez + de usar una estructura en XML, usa un lenguaje propio, facilitando enormemente la + lectura y modificación de dicho archivo. +

+ O Ibatis, para el acceso a base de datos. + Plasmando en este mismo fichero de confugración la configuración de acceso a datos, y + las queries que atacan a esta.
+ Una peculiaridad de QCore es la posibilidad de definir el engine de base de datos + usado, y poder realizar multiples queris para los distintos engines.
+ De esa manera QCore es capaz de decidir la query a usar dependiendo del engine del + acceso a datos, y por lo tanto facilitar el cambio de engine de una base de datos a + otra sin penalización en el desarroyo. +

+
+ +
+
Instalación:
+
+ Ejecutar en una Base de datos el script correspondiente a su engine, este generará + la estructura, y datos básicos para el uso de QCore.
+ QCore busca por defecto el archivo de contexto qcore.ctx dentro de META-INF. Se publican + unos archivos .ctx de ejemplo para la configuración inicial.
+ Añadir QCore.jar como libreria de aplicación, y añadir al META-INF de la aplicación + web los archivos de ejeplo situados en doc/META-INF.
+ Editar el web.xml, añadiendo el correspondiente filtro, es recomendable usar este filtro como primer filtro +
+<filter>
+  <filter-name>SessionFilter</filter-name>
+  <filter-class>qcore.WebFilter</filter-class>
+</filter>
+
+<filter-mapping>
+  <filter-name>SessionFilter</filter-name>
+  <url-pattern>*.do</url-pattern>
+</filter-mapping>
+<filter-mapping>
+  <filter-name>SessionFilter</filter-name>
+  <url-pattern>*.jsp</url-pattern>
+</filter-mapping>
+<filter-mapping>
+  <filter-name>SessionFilter</filter-name>
+  <url-pattern>*.html</url-pattern>
+</filter-mapping>
+
+
+
+ +
+
Lenguaje de contexto:
+
+ QCore utiliza un formato propio para definir una persistencia de objetos que son instanciados como objetos para definir + distintas configuraciones y estructuras complejas. + + QCore leera por defecto el archivo localizado en NETA-INF/qcore.ctx el cual ha de definirse en el proyecto de aplicación + +
Definición de Proveedores
+
+
 Estructura:
+  provider <name> default <defaultname>{
+   <implname>
+   <implname>
+  }
+ 
+ El nombre <name> ha de coincidir con el de la clase del proveedor (sin el paquete) + El nombre <defaultname> ha de estar en la lista de <implname> + Los proveedores adicionales han de estar listados en <implname> +
+ +
Definición de Objeto
+
+
 Estructura:
+  object [<name>] class <class>{[(<parameter>[,<parameter>,...])
+   [<property>=<value>]
+   [<property>=<value>]
+   [...]
+  }
+ 
+ El nombre <name> ha de ser único entre todas las definiciones del contexto, + excepto con las definiciones de queries. La clase <class> ha de un nombre + completo de una clase instanciable. El parametro <parameter> son los parametros + del constructor. Tanto <property> como <value> se usan para asignar + propiedades de la clase mediante su seter o directamente en la propiedad privada. +
+ +
Definición de Array
+
+
 Estructura:
+  array [<name>] of <class> {
+   [<value>]
+   [<value>]
+   [...]
+  }
+ 
+ El nombre <name> ha de ser único entre todas las definiciones del contexto, excepto con las definiciones de queries + El valor <value> se usa para definir los vlares del array +
+ +
Definición de Lista
+
+
 Estructura:
+  list [<name>]{
+   [<value>]
+   [<value>]
+   [...]
+  }
+ 
+ El nombre <name> ha de ser único entre todas las definiciones del contexto, excepto con las definiciones de queries + El valor <value> se usa para definir los vlares del array +
+ +
Definición de HashMap
+
+
 Estructura:
+  map [<name>] {
+   [{<key>,<value>}]
+   [{<key>,<value>}]
+   [...]
+  }
+ 
+ El nombre <name> ha de ser único entre todas las definiciones del contexto, excepto con las definiciones de queries + La clave <key> es la clave del HashMap + El valor <value> es el valor para la clave <key> +
+ +
Definición de null
+
+
 Estructura:
+  null
+ 
+ Es la definicion del valor null +
+ +
Definición de literal String
+
+
 Estructura:
+  "<value>"
+ 
+ El valor <value> es el contenido del String sin cambios. +
+ +
Definición de literal numerica
+
+
 Estructura:
+  <value>
+ 
+ El valor <value> es un número, usando el punto "." para decimales +
+ +
Definición de literal booleana
+
+
 Estructura:
+  true|false
+ 
+ Literal sin commillas, true para verdadero y false para falso. +
+ +
Definición de Queries
+
+
 Estructura:
+  query <name> [engine <enginename>|any] [type <type>] { <querystr> }
+ 
+ El engine ha de coincidir con el especificado en el acceso a datos (case sensitive) any para cualquera, + si no se especifica engine, se asume any.
+ Querystr puede contener información adicional usando la almoadilla como caracter de escape: +
    +
  • ## Hace referencia a un tipo escalar unico
  • +
  • #[<numero indice>]# Hace referencia a un elemento de un array Ex: #1#,#2#,#10#
  • +
  • #<nombre miembro># Hace referencia a una propiedad de un objeto, o a una clave en un hashmap Ex: #userName#,#userPwd#
  • +
+</div> +</div> +
+ +
+
+ +
+
Conocimientos básicos:
+
+ La base de datos por defecto de QCore contiene 2 grupos: Admin y Guest. Y un + usuario: admin (contraseña 1234), perteneciente al grupo Admin. +

+ El grupo guest es obigatorio, ya que es el grupo que se le asigna a los usuarios no logados. + es recomendable que no tenga permisos, pero que sea un gupo activo. +

+ El fichero de contexto ha de tener de forma obligatoria un objeto llamado QCoreConf. Ejemplo:
+

+object QCoreConf class nihao.QCoreConf{
+ loginConf=object class nihao.login.LoginConf{
+  modules=array of nihao.login.ILoginModule{
+   //object class nihao.login.LoginModuleSSL{}
+   object class nihao.login.LoginModuleRequest{}
+  };
+  pageLogin="login.jsp";
+  pageEnd=  "end.jsp";
+  pageError="error.jsp";"
+  pageForbidden="forbidden.jsp";
+  excluded=array of String{
+  };
+ }
+ worksetCacheSize=10;
+}
+ 
+
+Uso en JSP:
+ Se usa a través del taglib de QCore, defeinir en cada página:
+  <%@ taglib uri="http://www.xwolf.es/qcore" prefix="Q"%>
+  para usar el taglib
+
+Id en tags
+ Para que el tag coja datos del Workset se vinculan mediante el mismo nombre de campo del workset que el id del tag.
+ En este caso el tag es de ida y vuelta (el valor se envia a la página, pero este también se notifica al servidor).
+ Existen casos especiales, para obtener valores, pero estos no serán enviados al servidor de manera natural.
+ Estos casos son:
+  · Nombres que empiezan por "|": Obtienen el valor de los atributos del PageContext, el nombre en el PageContext incluye
+  el simbolo "|"
+  · Nombres que empiezan por "@": Obtienen el valor del archivo de contexto de aplicación, sin incluir el simbolo inicial. 
+  · Nombres que empiezan por "$": Obtienen el valor de la sesión, sin incluir el simbolo inicial. 
+  · Nombres que empiezan por "%": Obtienen el valor de el parametro del request, sin incluir el simbolo inicial.
+  · Puntos en el nombre: Si los nombres de campo estan separados por ".", se realiza una navegación. por
+  los elementos de los objetos, pudiendo emplear cualquier profundidad. Si algún elemento en la ruta de
+  navegación, es null, se retorna null.
+ Para enviar al servidor hay que especificar el attributo send="<nombre campo>". Dónde
+  <nombre campo> es el nombre del campo del workset.
+ 
+
+Proveedores de acceso a datos:
+ Estructura:
+  object MainDatabaseConnection class xwolf.qcore.db.DataSourceProviderJNDI{("java:comp/env/jdbc/<name>")
+   engine="<engine>" //opcional
+  }
+  object MainDatabaseConnection class xwolf.qcore.db.DataSourceProviderJDBC{("jdbc:<engine>://<host>:3306/<ddbb>")
+   driver="<driver to initialize>" // opcional
+   engine="<engine>" //opcional
+   user="<user>"
+   password="<password>"
+  }
+
+
+
+ +
+ + \ No newline at end of file diff --git a/NiHao/release/NiHao-0.8b.jar b/NiHao/release/NiHao-0.8b.jar new file mode 100644 index 0000000..ca2dd0c --- /dev/null +++ b/NiHao/release/NiHao-0.8b.jar Binary files differ diff --git a/NiHao/release/Recover NiHao-0.8b.jar b/NiHao/release/Recover NiHao-0.8b.jar new file mode 100644 index 0000000..ca2dd0c --- /dev/null +++ b/NiHao/release/Recover NiHao-0.8b.jar Binary files differ diff --git a/NiHao/src/nihao/Autowired.java b/NiHao/src/nihao/Autowired.java new file mode 100644 index 0000000..de8bc84 --- /dev/null +++ b/NiHao/src/nihao/Autowired.java @@ -0,0 +1,27 @@ +package nihao; + +/** + * Link:
+ * · interfaces with implementations
+ * · abstract class with instances of non abstract descendant
+ * · classes with instances of class
+ * + * @author XWolf + * + */ +public @interface Autowired { + /** + * + * If set uses this implementation as wire + * + */ + Class use() default Void.class; + + /** + * + * Wire descendant on first use + * + * @deprecated future use + */ + boolean lazy() default false; +} diff --git a/NiHao/src/nihao/NiHao.java b/NiHao/src/nihao/NiHao.java new file mode 100644 index 0000000..5ce95c5 --- /dev/null +++ b/NiHao/src/nihao/NiHao.java @@ -0,0 +1,249 @@ +package nihao; + +import java.util.HashMap; +import java.util.Map; + +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.servlet.ServletContext; + +import nihao.context.Context; +import nihao.context.FileContext; +import nihao.db.ChangesetDataManager; +import nihao.db.Query; +import nihao.util.Conversor; +import nihao.util.Resources; + +public class NiHao { + public static final String Version = "0.9"; + static Context ctx; + static NiHaoConf conf; + static Map initParameters; + static Map, Object> wired = new HashMap, Object>(); + static ServletContext servletContext; + + static { + Resources.addClassLoader(NiHao.class.getClassLoader()); + Resources.addClassLoader(Thread.currentThread().getContextClassLoader()); + init(); + } + + /** + * Inicia el core, este paso se realiza sólo una vez + */ + public static void init() { + if (ctx != null) + return; + Resources.addClassLoader(Thread.currentThread().getContextClassLoader()); + ctx = new FileContext("META-INF/nihao.ctx"); + ctx.commit(); + // Changesets + if (ctx.haveChangesets()) { + ChangesetDataManager cdm = new ChangesetDataManager(); + cdm.runChagesets(ctx); + } + } + + /** + * Obtiene un objeto desde el contexto + * + * @param name + * String + * @return Object + */ + public static Object get(String name) { + return ctx.get(name); + } + + /** + * Obtiene un objeto desde el contexto, con el tipo especifico, y con el + * nombre del tipo + * + * @param + * Tipo de retorno + * @param cls + * Clase del tipo de retonro + * @return Instncia del tipo del parametro cls + */ + public static T get(Class cls) { + return getAs(cls.getSimpleName(), cls); + } + + /** + * Obtiene un string desde el contexto + * + * @param name + * String + * @return String + */ + public static String getString(String name) { + Object result = ctx.get(name); + if (result == null) + return null; + return result.toString(); + } + + /** + * Obtiene un entero desde el contexto + * + * @param name + * String + * @return int + */ + public static int getInt(String name) { + return Integer.parseInt(getString(name)); + } + + /** + * Obtiene un entero largo desde el contexto + * + * @param name + * String + * @return long + */ + public static long getLong(String name) { + return Long.parseLong(getString(name)); + } + + /** + * Obtiene un doble desde el contexto + * + * @param name + * String + * @return double + */ + public static double getDouble(String name) { + return Double.parseDouble(getString(name)); + } + + /** + * Obtiene un objeto desde el contexto con el tipo especifico + * + * @param + * tipo de retorno + * @param name + * String + * @param cls + * Class clase re retorno + * @return + */ + public static T getAs(String name, Class cls) { + Object result = ctx.get(name); + if (result == null) + return null; + if (cls.isInstance(result)) + return cls.cast(result); + return null; + } + + /** + * Retona una query desde el contexto + * + * @param name + * String nombre de la query + * @return Query + */ + public static Query getQuery(String name, String engine) { + return ctx.getQuery(name, engine); + } + + /** + * Retorna la página diseñada para una URL determinada + * + * @param url + * String + * @return Page + */ + public static Page getPage(String url) { + if (!url.startsWith("/")) + url = "/" + url; + return ctx.getPage(url); + } + + /** + * Retorna true en caso de que el valor del bean sea positivo + * + * @param beanName + * String + * @return boolean + */ + public static boolean isYes(String beanName) { + return Conversor.isYes(getString(beanName)); + } + + /** + * Comprueba que a y b son iguales, usando su .equals, y controlando nulos + * + * @param a + * Object + * @param b + * Object + * @return boolean + */ + public static boolean equals(Object a, Object b) { + if (a == b) + return true; + if (a == null) + return false; + if (b == null) + return false; + return a.equals(b); + } + + public static NiHaoConf getConf() { + if (conf == null) + conf = get(NiHaoConf.class); + return conf; + } + + /** + * Retorna un EnvEntry + * + * @param key + * String + * @return String + */ + public static String getEnvEntry(String key) { + try { + javax.naming.Context miCtx = (javax.naming.Context) (new InitialContext()).lookup("java:comp/env"); + return (String) miCtx.lookup(key); + } catch (NamingException e) { + return null; + } + } + + /** + * Retorna un InitParameter + * + * @param key + * String + * @return String + */ + public static String getInitParameter(String key) { + if (initParameters.containsKey(key)) + return initParameters.get(key); + return null; + } + + /** + * Realiza el enlace de los Autowired de una instancia.
+ * Este método se usa tanto internamente para cada clase instanciada desde + * el contexto, como para iniciar los Autowired de un objeto ya instanciado. + * + * @param o + * Object objeto a autoenlazar + */ + public static void autoWire(Object o) { + if (o == null) + return; + } + + /** + * Return the application servlet context + * + * @return + */ + public static ServletContext getServletContext() { + return servletContext; + } +} diff --git a/NiHao/src/nihao/NiHaoConf.java b/NiHao/src/nihao/NiHaoConf.java new file mode 100644 index 0000000..2d2b0c8 --- /dev/null +++ b/NiHao/src/nihao/NiHaoConf.java @@ -0,0 +1,27 @@ +package nihao; + +import nihao.login.LoginConf; + +public class NiHaoConf { + private LoginConf loginConf; + private int worksetCacheSize = 2; + private String defaultGroupForUndefinedPage; + + public LoginConf getLoginConf() { + return loginConf; + } + + public int getWorksetCacheSize() { + return worksetCacheSize; + } + + /** + * Retorna el grupo por defecto de las páginas que no se definen en el + * contexto, si no se define un grupo se redirige a forbidden + * + * @return + */ + public String getDefaultGroupForUndefinedPage() { + return defaultGroupForUndefinedPage; + } +} diff --git a/NiHao/src/nihao/NiHaoException.java b/NiHao/src/nihao/NiHaoException.java new file mode 100644 index 0000000..a0ce834 --- /dev/null +++ b/NiHao/src/nihao/NiHaoException.java @@ -0,0 +1,17 @@ +package nihao; + +public class NiHaoException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NiHaoException(String message) { + super(message); + } + + public NiHaoException(String message, Throwable t) { + super(message, t); + } + + public NiHaoException(Throwable t) { + super(t); + } +} diff --git a/NiHao/src/nihao/Page.java b/NiHao/src/nihao/Page.java new file mode 100644 index 0000000..a1c9dd4 --- /dev/null +++ b/NiHao/src/nihao/Page.java @@ -0,0 +1,86 @@ +package nihao; + +import nihao.login.Group; +import nihao.login.User; +import nihao.types.PageSecuityType; + +public class Page { + private String url; + private String workset; + private String[] groupAllow; + private String[] groupDeny; + private PageSecuityType security; + private boolean regex; + + public Page(String url,PageSecuityType security) { + this.url = url; + this.security=security; + } + + public String getUrl() { + return url; + } + + public void setGroupAllow(String[] groupAllow) { + this.groupAllow = groupAllow; + } + + public void setGroupDeny(String[] groupDeny) { + this.groupDeny = groupDeny; + } + + public void setWorkset(String workset) { + this.workset = workset; + } + + public String getWorkset() { + return workset; + } + + private boolean inList(String name, String[] groups) { + for (String g : groups) + if (name.equalsIgnoreCase(g)) + return true; + return false; + } + + /** + * Retorna si un usuario puede entrar en una página a partir de los grupos + * definidos.
+ * Si un usuario no contiene ningún grupo permitido, no puede entrar.
+ * Si un usuario contiene algún grupo permitido y ninguno restringido, puede + * entrar.
+ * Si un usuario contiene algún grupo restringido, no puede entrar.
+ * + * @param user + * User + * @return + */ + public boolean allow(User user) { + if (security==PageSecuityType.PUBLIC) + return true; + if (security==PageSecuityType.PRIVATE) + return user!=SessionController.guestUser; + boolean allow = false; + for (Group g : user.getGroups()) { + if (groupDeny!=null && inList(g.getName(), groupDeny)) + return false; // la denegación es inmediata + if (groupAllow!=null && inList(g.getName(), groupAllow)) + allow = true; + } + return allow; + } + + public boolean isRegex() { + return regex; + } + + public void setRegex(boolean regex) { + this.regex = regex; + } + + @Override + public String toString() { + return "Page: "+(regex?"~":"")+url; + } +} diff --git a/NiHao/src/nihao/Provider.java b/NiHao/src/nihao/Provider.java new file mode 100644 index 0000000..3eca3e2 --- /dev/null +++ b/NiHao/src/nihao/Provider.java @@ -0,0 +1,11 @@ +package nihao; + +public abstract class Provider { + protected static T instanceAs(ProviderInfo info, Class cls) { + return NiHao.getAs(info.defaultProvider, cls); + } + + protected static T instanceAs(ProviderInfo info, String name, Class cls) { + return NiHao.getAs(name, cls); + } +} diff --git a/NiHao/src/nihao/ProviderInfo.java b/NiHao/src/nihao/ProviderInfo.java new file mode 100644 index 0000000..d39071d --- /dev/null +++ b/NiHao/src/nihao/ProviderInfo.java @@ -0,0 +1,42 @@ +package nihao; + +public class ProviderInfo { + + public static ProviderInfo get(Class provider) { + return get(provider, null); + } + + public static ProviderInfo get(Class provider, Class defaultProvider) { + ProviderInfo info = NiHao.getAs(provider.getSimpleName(), ProviderInfo.class); + if (info == null) + if (defaultProvider == null) + throw new NiHaoException("Can't locate provider " + provider.getSimpleName()); + else { + info = new ProviderInfo(provider.getSimpleName(), defaultProvider.getSimpleName(), new String[0]); + } + + return info; + } + + String providerName; + String defaultProvider; + String[] alternatives; + + public ProviderInfo(String name, String defaultProvider, String[] alternatives) { + providerName = name; + this.defaultProvider = defaultProvider; + this.alternatives = alternatives; + } + + public String getProviderName() { + return providerName; + } + + public String getDefaultProvider() { + return defaultProvider; + } + + public String[] getAlternatives() { + return alternatives; + } +} diff --git a/NiHao/src/nihao/SessionController.java b/NiHao/src/nihao/SessionController.java new file mode 100644 index 0000000..3e659be --- /dev/null +++ b/NiHao/src/nihao/SessionController.java @@ -0,0 +1,127 @@ +package nihao; + +import java.io.Serializable; + +import javax.servlet.http.HttpSession; + +import nihao.login.Group; +import nihao.login.ILoginModule; +import nihao.login.LoginDataManager; +import nihao.login.User; +import nihao.util.Conversor; + +public class SessionController implements Serializable { + private static final long serialVersionUID = 1L; + private static final String CTL_ID = "::QCORE:SESSION_CONTROLLER"; + private static final long USERCACHETIMEOUT = 60000; // 1 minuto, una vez que + // pase se rechequea el + // usuario (no se sale + // de la sesion) + + /** + * Usuario GUEST + */ + public static final Group guestGroup; + public static final User guestUser; + + static { + guestGroup=new Group(); + guestGroup.setId(-1); + guestGroup.setName("Guest"); + guestUser = new User(-1); + guestUser.setName("GUEST"); + guestUser.setNick("GUEST"); + guestUser.setGroups(new Group[] { guestGroup }); + } + + private String sessionId = null; + private long usercachetimectl = 0; + private User user; + + private SessionController(HttpSession session) { + sessionId = session.getId(); + } + + /** + * Retorna true si esta logado + * + * @return boolean + */ + public boolean isLogged() { + return user != null && user != guestUser; + } + + /** + * Realiza el proceso de login + * + * @param call + * WebCall + * @return boolean + */ + boolean login(WebCall call) { + if (isLogged()) { + if (System.currentTimeMillis() - usercachetimectl > USERCACHETIMEOUT) { + // Comprueba si el usuario sigue siendo válido + usercachetimectl = System.currentTimeMillis(); + LoginDataManager ldm = new LoginDataManager(); + user = ldm.getUserByNick(user.getNick()); + if (user == null) + return login(call); + user.setGroups(ldm.getUserGroups(user)); + } + return true; + } + try { + usercachetimectl = System.currentTimeMillis(); + ILoginModule[] lmods = NiHao.getConf().getLoginConf().getModules(); + for (ILoginModule lmod : lmods) { + user = lmod.login(call); + if (user != null) { + LoginDataManager ldm = new LoginDataManager(); + user.setGroups(ldm.getUserGroups(user)); + return true; + } + } + user = guestUser; + return false; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Obtiene el usuario en sesión + * + * @return + */ + public User getUser() { + return user; + } + + /** + * Recoge el seesionController de la sesión, si no hay crea una nueva + * + * @param session + * HttpSession + * @return WebSessionHandler + */ + public static SessionController getSessionController(HttpSession session) { + SessionController result = Conversor.as(session.getAttribute(CTL_ID), SessionController.class); + if (result == null) { + result = new SessionController(session); + session.setAttribute(CTL_ID, result); + } + return result; + } + + /** + * Obtiene el identificador de la sesion + * + * @return + */ + public String getSessionId() { + return sessionId; + } +} diff --git a/NiHao/src/nihao/WebCall.java b/NiHao/src/nihao/WebCall.java new file mode 100644 index 0000000..0a53229 --- /dev/null +++ b/NiHao/src/nihao/WebCall.java @@ -0,0 +1,427 @@ +package nihao; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.FilterChain; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import nihao.login.Group; +import nihao.login.LoginConf; +import nihao.login.User; +import nihao.util.Conversor; +import nihao.util.reflection.Reflector; + +public class WebCall { + private static final String LOGOUT_PARAMETER = "logout"; + private static final String INFO_ID = "::QCORE:INFO"; + private static final String ERROR_ID = "::QCORE:ERROR"; + public static ThreadLocal currentWebCall = new ThreadLocal(); + + /** + * Obtiene el WebCall de la ejecución a través del request + * + * @param request + * ServletRequest + * @return WebCall + */ + public static WebCall getWebCall(ServletRequest request) { + return (WebCall) request.getAttribute(""); + } + + /** + * Obtiene el WebCall en ejecucuón + * + * @return WebCall + */ + public static WebCall getWebCall() { + return currentWebCall.get(); + } + + HttpServletRequest rq; + HttpServletResponse rs; + FilterChain chain; + HttpSession ses; + String webUrlRoot; + String url; + boolean wantToLogOut; + SessionController sessionController; + Page page; + WorksetHandler workset; + WorksetController worksetController; + Map> violations; + + public WebCall(ServletRequest request, ServletResponse response, FilterChain chain) { + rq = (HttpServletRequest) request; + rs = (HttpServletResponse) response; + this.chain = chain; + webUrlRoot = rq.getContextPath() + "/"; + String pathInfo = rq.getPathInfo(); // Faces + String servletPath = rq.getServletPath(); + if (servletPath!=null) + url=servletPath; + else + url=""; + if (pathInfo!=null) + url+=pathInfo; + if (url.length()>0) + url = url.substring(1); + if (wantToLogOut = Conversor.isYes(getParameter(LOGOUT_PARAMETER))) { + ses = rq.getSession(false); + if (ses != null) + ses.invalidate(); + } else { + ses = rq.getSession(true); + sessionController = SessionController.getSessionController(ses); + worksetController = WorksetController.getWorksetController(ses); + } + rq.setAttribute("", this); + currentWebCall.set(this); + } + + /** + * Retorna un parametro del request + * + * @param key + * String nombre del parametro + * @return + */ + public String getParameter(String key) { + return rq.getParameter(key); + } + + /** + * Retorna un atributo del request + * + * @param key + * String nombre del atributo + * @return + */ + public Object getAttribute(String key) { + return rq.getAttribute(key); + } + + /** + * Retorna un valor almacenado en sesión + * + * @param key + * String clave + * @return Object valor + */ + public Object getSession(String key) { + return ses.getAttribute(key); + } + + /** + * Obtiene un valor con el formato usado en los tags de la web, intenta + * parsear los puntos para obtener los datos hijos + * + * @param name + * String clave del valor, 4 tipos:
+ * · si se antepone "@" se retorna un valor de contexto
+ * · si se antepone "$" se retorna un valor de sesión
+ * · si se antepone "%" se retorna un valor de atributo + * del request
+ * en cualquier otro caso, el campo hace referencia a un valor en + * el workset de la página + * @return + */ + public Object getValue(String name) { + if (name == null || name.length() == 0) + return null; + if (name.contains(".")) { + String[] names = name.split("\\."); + Object o = getDirectValue(names[0]); + if (o == null) + return null; + return Reflector.getPathValue(o, names, 1, Object.class); + } else + return getDirectValue(name); + } + + /** + * Coge el valor del workset sin parsear los puntos + * + * @param name + * String clave del valor, 4 tipos:
+ * · si se antepone "@" se retorna un valor de contexto
+ * · si se antepone "$" se retorna un valor se desión
+ * · si se antepone "%" se retorna un valor de atributo + * del request
+ * en cualquier otro caso, el campo hace referencia a un valor en + * el workset de la página + * @return + */ + public Object getDirectValue(String name) { + if (name == null || name.length() == 0) + return null; + if (name.startsWith("@")) { + return NiHao.get(name.substring(1)); + } else if (name.startsWith("$")) { + return getSession(name.substring(1)); + } else if (name.startsWith("%")) { + return getAttribute(name.substring(1)); + } + if (workset == null) + throw new NiHaoException("page '/" + url + "' don't have a workset"); + return workset.get(name, this); + } + + /** + * Retonra una URL con la base de aplicación + * + * @param url + * @return + */ + public String getFullUrl(String url) { + if (url.startsWith("/")) + url = url.substring(1); + return webUrlRoot + url; + } + + /** + * Retorna la url actual con la base de aplicación + * + * @return + */ + public String getFullUrl() { + return webUrlRoot + url; + } + + /** + * Retorna la url actual (sin la base de aplicación, ni "/" inicial) + * + * @return + */ + public String getUrl() { + return url; + } + + private void go(String url) throws IOException, ServletException { + if (url.startsWith(webUrlRoot)) + url = url.substring(webUrlRoot.length() - 1); + if (ses != null) + ses.setAttribute("@@LAST", url); + RequestDispatcher rdispatcher = rq.getRequestDispatcher(url); + rdispatcher.forward(rq, rs); + } + + /** + * Va a la página de cierre de sesión + */ + public void goEnd() throws IOException, ServletException { + go(NiHao.getConf().getLoginConf().getPageEnd()); + } + + /** + * Va a la página de error + */ + public void goError(Throwable t) throws IOException, ServletException { + rq.setAttribute(ERROR_ID, t); + go(NiHao.getConf().getLoginConf().getPageError()); + } + + /** + * Va a la página de Login + */ + public void goLogin() throws IOException, ServletException { + go(NiHao.getConf().getLoginConf().getPageLogin()); + } + + /** + * Va a la página de Prohibido + */ + public void goForbidden() throws IOException, ServletException { + go(NiHao.getConf().getLoginConf().getPageForbidden()); + } + + /** + * Continua sin redireccion, no se procesa la llamada por elframework + * + * @throws IOException + * @throws ServletException + */ + public void goExcluded() throws IOException, ServletException { + chain.doFilter(rq, rs); + } + + /** + * Chequea el flujo de validaciones, y continua sin redireccion o va para + * atras. + * + * @throws IOException + * @throws ServletException + */ + public void go() throws IOException, ServletException { + obtainWorkset(); + if (haveValidationViolations()) { + goBack(); + return; + } + ses.setAttribute("@@LAST", url); + chain.doFilter(rq, rs); + } + + /** + * Cancela la navegación + * + * @throws IOException + * @throws ServletException + */ + public void goBack() throws IOException, ServletException { + if (ses == null) { + goRoot(); + return; + } + String to = (String) ses.getAttribute("@@LAST"); + if (to == null) { + goRoot(); + return; + } + go(to); + } + + /** + * Se dirige al indice del sitio. + */ + public void goRoot() throws IOException, ServletException { + go(""); + } + + boolean login() { + return sessionController.login(this); + } + + public X509Certificate[] getSSLCertificates(String sslCertificateAttributeName) { + return (X509Certificate[]) rq.getAttribute(sslCertificateAttributeName); + } + + public boolean canEnterThePage() { + page = NiHao.getPage(url); + if (page == null) { + String group = NiHao.getConf().getDefaultGroupForUndefinedPage(); + if (group == null) { + rq.setAttribute(INFO_ID, "Unaccessible page ever."); + return false; + } + if ("*".equals(group)) + return true; + for (Group g : sessionController.getUser().getGroups()) + if (group.equalsIgnoreCase(g.getName())) + return true; + return false; + } + return page.allow(sessionController.getUser()); + } + + public void obtainWorkset() { + if (page != null && page.getWorkset() != null) + workset = worksetController.get(page.getWorkset()); + if (workset != null) + workset.execute(rq.getParameterMap(), this); + } + + public WorksetHandler getWorkset() { + return workset; + } + + /** + * True if page is excluded, excluded pages will no have any login + * + * @return true si la página no requiere seguridad + */ + public boolean isExcluded() { + LoginConf conf = NiHao.getConf().getLoginConf(); + if (conf.isDisabled()) + return true; + if (conf.getExcluded() == null) + return false; + for (String ex : conf.getExcluded()) + if (ex.equalsIgnoreCase(url)) + return true; + return false; + } + + /** + * Retorna un error + * + * @return + */ + public Throwable getError() { + return (Throwable) rq.getAttribute(ERROR_ID); + } + + /** + * Retorna un info + * + * @return + */ + public String getInfo() { + return (String) rq.getAttribute(INFO_ID); + } + + /** + * Retorna el usuario en sesión + * + * @return User + */ + public User getUser() { + if (sessionController == null) + return null; + return sessionController.getUser(); + } + + /** + * Añade una violación de validación, si hay alguna violación de validacion + * la página no coninuará.
+ * Para que se muestren las violaciones en la página, hay que utilizar el + * tag <Q:Violation /> + * + * @param name + * String. Nombre de la validación violada (nombre de campo) + * @param violation + * String. Descripción de la violación. + */ + public void addValidationViolation(String name, String violation) { + if (violations == null) + violations = new HashMap>(); + if (!violations.containsKey(name)) + violations.put(name, new ArrayList()); + violations.get(name).add(violation); + } + + /** + * Retorna las violaciones definidas para un nombre + * + * @param name + * Nombre de la validación violada + * @return String[] o null en caso de no existir violaciones + */ + public String[] getValidationViolations(String name) { + if (violations == null) + return null; + if (!violations.containsKey(name)) + return null; + ArrayList vios = violations.get(name); + return vios.toArray(new String[vios.size()]); + } + + /** + * Retorna true en caso de que se haya producido algún tipo de violación de + * validaión. + * + * @return True en caso de que haya validaciones violadas. + */ + public boolean haveValidationViolations() { + return violations != null; + } +} diff --git a/NiHao/src/nihao/WebFilter.java b/NiHao/src/nihao/WebFilter.java new file mode 100644 index 0000000..eff1d42 --- /dev/null +++ b/NiHao/src/nihao/WebFilter.java @@ -0,0 +1,61 @@ +package nihao; + +import java.io.IOException; +import java.util.Enumeration; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +@javax.servlet.annotation.WebFilter(filterName = "SessionFilter", urlPatterns = "/*") +public class WebFilter implements Filter { + + @Override + public void destroy() { + } + + public void doFilter(ServletRequest srequest, ServletResponse sresponse, + FilterChain chain) throws IOException, ServletException { + WebCall call = new WebCall(srequest, sresponse, chain); + try { + if (call.wantToLogOut) + call.goEnd(); + else if (call.isExcluded()) + call.goExcluded(); + else if (call.sessionController.isLogged()) + enterPage(call, false); + else if (call.login()) + enterPage(call, false); + else + enterPage(call, true); + } catch (Throwable t) { + call.goError(t); + } + } + + private void enterPage(WebCall call, boolean tologin) throws IOException, + ServletException { + boolean icango = call.canEnterThePage(); + if (icango) + call.go(); + else if (tologin) + call.goLogin(); + else + call.goForbidden(); + } + + public void init(FilterConfig cfg) throws ServletException { + ServletContext context = cfg.getServletContext(); + NiHao.servletContext=context; + Enumeration e = context.getInitParameterNames(); + String key = null; + while (e.hasMoreElements()) { + key = e.nextElement().toString(); + NiHao.initParameters.put(key, context.getInitParameter(key)); + } + } +} diff --git a/NiHao/src/nihao/Workset.java b/NiHao/src/nihao/Workset.java new file mode 100644 index 0000000..64707ff --- /dev/null +++ b/NiHao/src/nihao/Workset.java @@ -0,0 +1,7 @@ +package nihao; + +import java.io.Serializable; + +public class Workset implements Serializable{ + private static final long serialVersionUID = 1L; +} diff --git a/NiHao/src/nihao/WorksetController.java b/NiHao/src/nihao/WorksetController.java new file mode 100644 index 0000000..27fc5fb --- /dev/null +++ b/NiHao/src/nihao/WorksetController.java @@ -0,0 +1,34 @@ +package nihao; + +import java.io.Serializable; + +import javax.servlet.http.HttpSession; + +import nihao.util.Conversor; +import nihao.util.LRUCache; + +public class WorksetController implements Serializable { + private static final long serialVersionUID = 1L; + private static final String CTL_ID = "::QCORE:WORKSET_CONTROLLER"; + private LRUCache cache = new LRUCache(NiHao.getConf().getWorksetCacheSize()); + + private WorksetController() { + } + + public static WorksetController getWorksetController(HttpSession session) { + WorksetController result = Conversor.as(session.getAttribute(CTL_ID), WorksetController.class); + if (result == null) { + result = new WorksetController(); + session.setAttribute(CTL_ID, result); + } + return result; + } + + public WorksetHandler get(String name) { + if (cache.containsKey(name)) + return cache.get(name); + WorksetHandler r = new WorksetHandler(name); + cache.put(name, r); + return r; + } +} diff --git a/NiHao/src/nihao/WorksetHandler.java b/NiHao/src/nihao/WorksetHandler.java new file mode 100644 index 0000000..c8eaea2 --- /dev/null +++ b/NiHao/src/nihao/WorksetHandler.java @@ -0,0 +1,191 @@ +package nihao; + +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import nihao.util.reflection.Reflector; + +public class WorksetHandler implements Serializable { + private static final long serialVersionUID = 1L; + transient private HashMap get = new HashMap(); + transient private HashMap put = new HashMap(); + transient private HashMap run = new HashMap(); + + private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + get = new HashMap(); + put = new HashMap(); + run = new HashMap(); + } + + private String wname; + private Workset w; + private Class wc; + + @SuppressWarnings("unchecked") + public WorksetHandler(String name) { + wname = name; + Object o = NiHao.get(name); + if (o == null) + throw new NiHaoException("Workset '" + name + "' dont exist!"); + wc = (Class) o.getClass(); + if (!(o instanceof Workset)) + throw new NiHaoException("Workset '" + name + "' class '" + wc.getCanonicalName() + "' must extend Workset"); + w = (Workset) o; + } + + /** + * Invoca el getter del workset, el getter puede tener definido + * adicionalmente un parametro de tipo WebCall, para pasarle la llamada a la + * hora de realizar el getter. + * + * @param name + * String. Nombre de la propiedad a leer + * @param cw + * WebCall actual (sera pasado en la invocación, si esta lo + * define) + * @return Object, Valor retornado por el getter. + */ + public Object get(String name, WebCall cw) { + Method m; + if (get.containsKey(name)) + m = get.get(name); + else { + m = Reflector.getGetter(wc, name); + if (m == null) + throw new NiHaoException("Workset '" + wname + "' dont have getter for property '" + name + "'!"); + get.put(name, m); + } + try { + Class[] pars = m.getParameterTypes(); + if (pars.length == 0) + return m.invoke(w); + if (pars.length == 1) + if (pars[0].isAssignableFrom(WebCall.class)) + return m.invoke(w, wc); + throw new NiHaoException("Unknown parameters in workset getter, only none or WebCall supported"); + } catch (IllegalArgumentException e) { + throw new NiHaoException(e); + } catch (IllegalAccessException e) { + throw new NiHaoException(e); + } catch (InvocationTargetException e) { + throw new NiHaoException(e); + } + } + + /** + * Invoca el setter del workset, es obligación que los setters se definan en + * el workset de la página.
+ * Adicionalmente se puede definir un parametro WebCall, en el que se pasara + * el WebCall de la página. + * + * @param name + * nombre de propiedad + * @param value + * valor a poner + */ + public void put(String name, Object value) { + Method m; + if (put.containsKey(name)) + m = put.get(name); + else { + m = Reflector.getSetter(wc, name); + if (m == null) + throw new NiHaoException("Workset '" + wname + "' dont have setter for property '" + name + "'!"); + put.put(name, m); + } + try { + Class[] pars = m.getParameterTypes(); + if (pars.length == 1) { + m.invoke(w, value); + return; + } + if (pars.length == 2) + if (pars[0].isAssignableFrom(WebCall.class)) { + m.invoke(w, wc, value); + return; + } else if (pars[1].isAssignableFrom(WebCall.class)) { + m.invoke(w, value, wc); + return; + } + throw new NiHaoException("Unknown parameters in workset setter. Only (), (,WebCall) or (WebCall,) supported"); + } catch (IllegalArgumentException e) { + throw new NiHaoException(e); + } catch (IllegalAccessException e) { + throw new NiHaoException(e); + } catch (InvocationTargetException e) { + throw new NiHaoException(e); + } + } + + /** + * Ejecuta un método del workset + * + * @param name + * Nombre del método + * @param call + * WebCall de la llamada, que se pasará al método + */ + public void run(String name, WebCall call) { + Method m; + if (run.containsKey(name)) + m = run.get(name); + else { + m = Reflector.getCompatibleMethod(wc, name, WebCall.class); + if (m == null) + throw new NiHaoException("Workset '" + wname + "' dont have callable method '" + name + "'!"); + run.put(name, m); + } + try { + m.invoke(w, call); + } catch (IllegalArgumentException e) { + throw new NiHaoException(e); + } catch (IllegalAccessException e) { + throw new NiHaoException(e); + } catch (InvocationTargetException e) { + Throwable t = e.getCause(); + if (t == null) + throw new NiHaoException(e); + if (t instanceof NiHaoException) + throw (NiHaoException) t; + throw new NiHaoException(t); + } + } + + /** + * Ejecuta el workset (realiza los setters y los invoke especificados en los + * parametros).
+ * Los parametros han de empezar por "::" para que los identifique el + * workset como propios. el resto de parametros no serán procesados por el + * setter del workset.
+ * Los parametros que empiezen por "@" notifican que se invocarán esos + * métodos del workset despues de terminar con los setters. Se ejecutarán en + * el orden definido. + * + * @param map + * Map, Mapa con los parametros en forma de String, String[] + * @param call + * WebCall de la página + */ + public void execute(Map map, WebCall call) { + ArrayList toRun = new ArrayList(5); + // Setters (::) + for (String k : map.keySet()) + if (k.startsWith("::")) { + String[] v = map.get(k); + if (v.length == 1) + put(k.substring(2), v[0]); + else + put(k.substring(2), v); + } else if (k.startsWith("@")) + toRun.add(k.substring(1)); + // Invokes (@) + for (String k : toRun) + run(k, call); + } +} diff --git a/NiHao/src/nihao/ajax/AjaxObject.java b/NiHao/src/nihao/ajax/AjaxObject.java new file mode 100644 index 0000000..e57bc6a --- /dev/null +++ b/NiHao/src/nihao/ajax/AjaxObject.java @@ -0,0 +1,109 @@ +package nihao.ajax; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.HashMap; +import java.util.Set; + +import nihao.NiHaoException; +import nihao.util.Serializer; +import nihao.util.reflection.Classes; + +public class AjaxObject { + private static HashMap objects = new HashMap(); + private Class c; + private Object o; + private HashMap methods; + static { + for (Class c : Classes.getLocalClassesWithAnnotation(AjaxPublished.class)) + Register(c); + } + + public static void Register(Class c) { + String name = c.getSimpleName(); + AjaxObject obj = objects.get(name); + if (obj != null) + throw new NiHaoException("Duplicated object. " + c.getName() + " was also registered with class " + obj.c.getName()); + objects.put(name, new AjaxObject(c)); + } + + static AjaxObject get(String name) { + return objects.get(name); + } + + private AjaxObject(Class c) { + this.c = c; + try { + o = c.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new NiHaoException("Can't create instance of ajax object " + getName(), e); + } + if (methods == null) { + methods = new HashMap(); + for (Method m : c.getMethods()) + if (m.getAnnotationsByType(AjaxPublished.class).length > 0) + methods.put(m.getName(), m); + } + } + + public String getName() { + return c.getSimpleName(); + } + + /** + * Return the ajax published methods + * + * @return + */ + public String[] getPublicMethods() { + Set keys = methods.keySet(); + return keys.toArray(new String[keys.size()]); + } + + /** + * Return the parameter names of a method + * + * @param key + * String method name + * @return + */ + public String[] getPublicMethodParams(String key) { + Method m = methods.get(key); + if (m == null) + return null; + Parameter[] pars = m.getParameters(); + String[] result = new String[pars.length]; + for (int i = 0; i < pars.length; i++) + result[i] = pars[i].getName(); + return result; + } + + /** + * Invokes a object method + * + * @param name + * String method name + * @param request + * String arguments in JSON object + * @return + */ + public Object invoke(String name, String request) { + Method m = methods.get(name); + if (m == null) + return new NiHaoException("Ajax method not exists"); + HashMap typeMap = new HashMap(); + Parameter[] pars = m.getParameters(); + for (Parameter p : pars) + typeMap.put(p.getName(), p.getParameterizedType()); + HashMap req = Serializer.deserializeFromJSON(request, null, typeMap); + Object[] args = new Object[pars.length]; + for (int i = 0; i < pars.length; i++) + args[i] = req.get(pars[i].getName()); + try { + return m.invoke(o, args); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new NiHaoException(e); + } + } +} diff --git a/NiHao/src/nihao/ajax/AjaxPublished.java b/NiHao/src/nihao/ajax/AjaxPublished.java new file mode 100644 index 0000000..bcce268 --- /dev/null +++ b/NiHao/src/nihao/ajax/AjaxPublished.java @@ -0,0 +1,9 @@ +package nihao.ajax; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface AjaxPublished { + //String name() default ""; +} diff --git a/NiHao/src/nihao/ajax/AjaxSerializer.java b/NiHao/src/nihao/ajax/AjaxSerializer.java new file mode 100644 index 0000000..7585f87 --- /dev/null +++ b/NiHao/src/nihao/ajax/AjaxSerializer.java @@ -0,0 +1,17 @@ +package nihao.ajax; + +import java.io.PrintWriter; + +public class AjaxSerializer { + static void printJavascriptSerializer(PrintWriter out) { + out.print("function(data){"); + out.print("return JSON.stringify(data);"); + out.print("}"); + } + + static void printJavascriptDeserializer(PrintWriter out) { + out.print("function(data){"); + out.print("try{return data.length==0?undefined:JSON.parse(data);}catch(e){throw new Error(data);}"); + out.print("}"); + } +} diff --git a/NiHao/src/nihao/ajax/WebAjax.java b/NiHao/src/nihao/ajax/WebAjax.java new file mode 100644 index 0000000..c7a3c4e --- /dev/null +++ b/NiHao/src/nihao/ajax/WebAjax.java @@ -0,0 +1,106 @@ +package nihao.ajax; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import nihao.util.IoUtil; +import nihao.util.Serializer; + +@WebServlet(name = "NiHao", urlPatterns = "/nihao/*") +public class WebAjax extends HttpServlet { + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + processRequest(request, response); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + processRequest(request, response); + } + + private void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String url = request.getPathInfo(); + if (url.endsWith(".js")) { + url = url.substring(1, url.length() - 3); + AjaxObject o = AjaxObject.get(url); + PrintWriter out = response.getWriter(); + if (o != null) + pushObjectJS(out, o, request.getContextPath() + request.getServletPath()); + else + out.print("Error"); + } else if (url.contains(".")) { + int point = url.indexOf('.'); + String method = url.substring(point + 1); + url = url.substring(1, point); + AjaxObject o = AjaxObject.get(url); + if (o == null) + response.getWriter().print("Call Error"); + else + try { + Object result = o.invoke(method, IoUtil.readStringToEnd(request.getReader())); + response.getWriter().write(Serializer.serializeToJSON(result)); + } catch (Throwable t) { + response.getWriter().print("Error " + t.getClass().getName() + ": " + t.getMessage()); + } + } else + response.getWriter().print("Error"); + } + + private void pushObjectJS(PrintWriter out, AjaxObject o, String basepath) throws IOException { + String oname = o.getName(); + out.print("var "); + out.print(oname); + out.println(" = new (function(){"); + out.print("var enc="); + AjaxSerializer.printJavascriptSerializer(out); + out.print(";var dec="); + AjaxSerializer.printJavascriptDeserializer(out); + out.print(";var call=function(mth,args){"); + out.print("xhr = new XMLHttpRequest();"); + out.print("xhr.open('post', '"); + out.print(basepath); + out.print("/"); + out.print(oname); + out.print(".'+mth, false);"); + out.print("xhr.send(enc(args));"); + out.print("return dec(xhr.responseText);"); + out.println("};"); + for (String s : o.getPublicMethods()) { + String[] pars = o.getPublicMethodParams(s); + out.print("this."); + out.print(s); + out.print("=function("); + boolean first = true; + for (String param : pars) { + if (first) + first = false; + else + out.print(", "); + out.print(param); + } + out.print("){return call('"); + out.print(s); + out.print("',{"); + first = true; + for (String param : pars) { + if (first) + first = false; + else + out.print(", "); + out.print(param); + out.print(": "); + out.print(param); + } + out.println("});}"); + } + out.println("})()"); + } +} diff --git a/NiHao/src/nihao/context/Context.java b/NiHao/src/nihao/context/Context.java new file mode 100644 index 0000000..d2b0bd6 --- /dev/null +++ b/NiHao/src/nihao/context/Context.java @@ -0,0 +1,713 @@ +package nihao.context; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map.Entry; + +import nihao.NiHao; +import nihao.NiHaoException; +import nihao.Page; +import nihao.context.StructureItemValue.Type; +import nihao.db.Changeset; +import nihao.db.Query; +import nihao.tokenizer.QToken; +import nihao.tokenizer.QTokenType; +import nihao.tokenizer.QTokenizer; +import nihao.util.Conversor; +import nihao.util.QDB; +import nihao.util.Resources; + +public abstract class Context { + private static final String[] CONTEXTPARSE_ELEMENTS = { "object", "val", "map", "list", "array", "jndi", "provider", "include", "query", "page", "workset", "changeset" }; + + ArrayList structure = new ArrayList(); + ArrayList fullstructure = new ArrayList(); + private HashMap singletons; + private HashMap> queries = new HashMap>(); + private HashMap pages = new HashMap(); + private ArrayList regexPages = new ArrayList(); + private StructureItemNull itemNull = new StructureItemNull(this); + private ArrayList changesets = new ArrayList(); + + /** + * Realiza el parsing de la estructura de contexto + * + * @param s + */ + protected void parse(String s) { + QTokenizer tkn = new QTokenizer(s); + StructureItem i; + while ((i = readNextElement(tkn)) != null) { + if (i.id == null) + throw new ContextParseException("Can't have anonymous element on root for " + i.getClass().getName()); + StructureItem existant = getStructure(i.id); + boolean duplicate = existant != null; + if (duplicate) { + if (i instanceof StructureItemQuery && existant instanceof StructureItemQuery) { + StructureItemQuery iq = (StructureItemQuery) i; + StructureItemQuery iq2 = (StructureItemQuery) existant; + if (NiHao.equals(iq.engine, iq2.engine)) + throw new ContextParseException("Duplicate query name " + i.id + " for engine " + Conversor.nvl(iq.engine, "any")); + else + duplicate = false; + } else if (i instanceof StructureItemChangeset && existant instanceof StructureItemChangeset) { + StructureItemChangeset iq = (StructureItemChangeset) i; + StructureItemChangeset iq2 = (StructureItemChangeset) existant; + if (NiHao.equals(iq.engine, iq2.engine)) + throw new ContextParseException("Duplicate changeset name " + i.id + " for engine " + Conversor.nvl(iq.engine, "any")); + else + duplicate = false; + } + } + if (duplicate) + throw new ContextParseException("Duplicate element name " + i.id); + structure.add(i); + } + } + + private int search(String element, String[] range) { + for (int i = 0; i < range.length; i++) + if (element.equals(range[i])) + return i; + return -1; + } + + private StructureItem readNextElement(QTokenizer tkn) { + QToken t = tkn.next(); + if (t == null) + return null; + if ("multi".equalsIgnoreCase(t.getValue())) { + StructureItem i = readNextElement(tkn); + i.singleton = false; + return i; + } + if ("null".equals(t.getValue())) + return itemNull; + if (t.getType() == QTokenType.LITERAL) + return new StructureItemValue(this, null, t.getValue(), Type.STRING); + if (t.getType() == QTokenType.NUMBER) + return new StructureItemValue(this, null, t.getValue(), Type.NUMBER); + if ("@".equals(t.getValue())) + return new StructureItemReference(this, null, tkn.next().getValue()); + if ("true".equalsIgnoreCase(t.getValue())) + return new StructureItemValue(this, null, "true", Type.BOOL); + if ("false".equalsIgnoreCase(t.getValue())) + return new StructureItemValue(this, null, "false", Type.BOOL); + if (t.getType() != QTokenType.WORD) + throw new RuntimeException("Error de configuración de contexto: " + t.getValue()); + String etype = t.getValue().toLowerCase(); + switch (search(etype, CONTEXTPARSE_ELEMENTS)) { + case 0: + return readObject(tkn); + case 1: + return readValue(tkn); + case 2: + return readMap(tkn); + case 3: + return readList(tkn); + case 4: + return readArray(tkn); + case 5: + return readJndi(tkn); + case 6: + return readProvider(tkn); + case 7: { + readInclude(tkn); + return readNextElement(tkn); + } + case 8: + return readQuery(tkn); + case 9: + return readPage(tkn); + case 10: + return readWorkset(tkn); + case 11: + return readChangeset(tkn); + default: + throw new ContextParseException("Unknown type " + etype); + } + } + + private StructureItem readJndi(QTokenizer tkn) { + throw new ContextParseException("Unimplemented"); + } + + private StructureItem readArray(QTokenizer tkn) { + QToken t = tkn.next(); + String id = t.getValue().toLowerCase(); + if ("of".equals(t.getValue())) + id = null; + else if (!"of".equals(tkn.next().getValue())) + throw new ContextParseException("'of' mistach"); + String className = elementReadClassName(tkn); + ArrayList list = new ArrayList(); + while (!"}".equals(tkn.patrol().getValue())) { + list.add(readNextElement(tkn)); + } + tkn.next(); + return new StructureItemArray(this, id, className, list.toArray(new StructureItem[list.size()])); + } + + private StructureItem readList(QTokenizer tkn) { + QToken t = tkn.next(); + String id = t.getValue().toLowerCase(); + StructureItemList result; + if (!"{".equals(t.getValue())) { + result = new StructureItemList(this, id); + if (!"{".equals(tkn.next().getValue())) + throw new ContextParseException("'{' mistach"); + } else + result = new StructureItemList(this, null); + while (!"}".equals(tkn.patrol().getValue())) + result.add(readNextElement(tkn)); + tkn.next(); + return result; + } + + private StructureItem readMap(QTokenizer tkn) { + QToken t = tkn.next(); + String id = t.getValue().toLowerCase(); + StructureItemMap result; + if (!"{".equals(t.getValue())) { + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("Identifier error: " + t.getValue()); + result = new StructureItemMap(this, id); + if (!"{".equals(tkn.next().getValue())) + throw new ContextParseException("'{' mistach"); + } else + result = new StructureItemMap(this, null); + t = tkn.next(); + while (!"}".equals(t.getValue())) { + if (!"{".equals(t.getValue())) + throw new ContextParseException("'{' mistach"); + StructureItem k = readNextElement(tkn); + if (!",".equals(tkn.next().getValue())) + throw new ContextParseException("',' mistach"); + StructureItem v = readNextElement(tkn); + if (!"}".equals(tkn.next().getValue())) + throw new ContextParseException("'}' mistach"); + result.putMap(k, v); + t = tkn.next(); + } + return result; + } + + private StructureItem readObject(QTokenizer tkn) { + QToken t = tkn.next(); + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("Identifier error: " + t.getValue()); + String id = t.getValue().toLowerCase(); + StructureItemObject result; + if (!"class".equals(id)) { + result = new StructureItemObject(this, id); + t = tkn.next(); + if (!t.getValue().equals("class")) + throw new ContextParseException("'class' keyword excepted, found " + t.getValue()); + } else + result = new StructureItemObject(this, null); + String className = elementReadClassName(tkn); + result.setObjectClass(className); + t = tkn.next(); + if ("(".equals(t.getValue())) { + do { + result.addConstructor(readNextElement(tkn)); + t = tkn.next(); + if (")".equals(t.getValue())) + break; + else if (!",".equals(t.getValue())) + throw new ContextParseException("Constructor object separator mistach"); + } while (true); + t = tkn.next(); + } + while (!"}".equals(t.getValue())) { + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("Unssuported " + t.getValue()); + String paramname = t.getValue(); + t = tkn.next(); + if (!"=".equals(t.getValue())) + throw new ContextParseException("Equals mistach"); + result.setParam(paramname, readNextElement(tkn)); + t = tkn.next(); + if (!";".equals(t.getValue())) + throw new ContextParseException("Missing ; for object '" + result.id + "' of class '" + className + "' in field '" + paramname + "' definition"); + t = tkn.next(); + } + return result; + } + + private StructureItem readValue(QTokenizer tkn) { + QToken t = tkn.next(); + if (t == null) + throw new ContextParseException("Unexpected end of script"); + if (t.getType() == QTokenType.LITERAL) + return new StructureItemValue(this, null, t.getValue(), Type.STRING); + if (t.getType() == QTokenType.NUMBER) + return new StructureItemValue(this, null, t.getValue(), Type.NUMBER); + if ("true".equalsIgnoreCase(t.getValue())) + return new StructureItemValue(this, null, "true", Type.BOOL); + if ("false".equalsIgnoreCase(t.getValue())) + return new StructureItemValue(this, null, "false", Type.BOOL); + String id = t.getValue().toLowerCase(); + t = tkn.next(); + if (!"=".equals(t.getValue())) + throw new ContextParseException("Equals mistach"); + t = tkn.next(); + StructureItemValue result; + if (t.getType() == QTokenType.LITERAL) + result = new StructureItemValue(this, id, t.getValue(), Type.STRING); + else if (t.getType() == QTokenType.NUMBER) + result = new StructureItemValue(this, id, t.getValue(), Type.NUMBER); + else if ("true".equalsIgnoreCase(t.getValue())) + result = new StructureItemValue(this, null, "true", Type.BOOL); + else if ("false".equalsIgnoreCase(t.getValue())) + result = new StructureItemValue(this, null, "false", Type.BOOL); + else + throw new ContextParseException("Syntax Error"); + if (!";".equals(tkn.next().getValue())) + throw new ContextParseException("; Mistach"); + return result; + } + + private StructureItem readProvider(QTokenizer tkn) { + QToken t = tkn.next(); + if (t == null) + throw new ContextParseException("Unexpected end of script"); + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("Identifier type error: " + t.getValue()); + StructureItemProvider result = new StructureItemProvider(this, t.getValue()); + if (!"default".equals(tkn.next().getValue())) + throw new ContextParseException("'default' mistach"); + t = tkn.next(); + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("default name type error, can't be: " + t.getValue()); + result.setDefault(t.getValue()); + if (!"{".equals(tkn.next().getValue())) + throw new ContextParseException("'{' mistach"); + ArrayList list = new ArrayList(); + while (!"}".equals(tkn.patrol().getValue())) { + t = tkn.next(); + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("alternative name type error, can't be: " + t.getValue()); + list.add(t.getValue()); + } + tkn.next(); // } final + result.setAuxiliar(list.toArray(new String[list.size()])); + return result; + } + + private void readInclude(QTokenizer tkn) { + QToken t = tkn.next(); + if (t.getType() != QTokenType.LITERAL) + throw new ContextParseException("filename literal mistach"); + String path = t.getValue(); + t = tkn.next(); + if (!";".equals(t.getValue())) + throw new ContextParseException("Missing ; for include '" + path + "' definition"); + + InputStream input = Resources.getResourceAsStream(path); + if (input == null) + input = Resources.getResourceAsStream("META-INF/" + path); + if (input == null) + throw new NiHaoException("Can't open context file: " + path); + String includestr = Conversor.readToString(input); + parse(includestr); + } + + private StructureItem readQuery(QTokenizer tkn) { + QToken t = tkn.next(); + if (t == null) + throw new ContextParseException("Unexpected end of script"); + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("Invalid query name"); + String name = t.getValue(); + if ("engine".equals(name)) + throw new ContextParseException("Query name mistach"); + if ("type".equals(name)) + throw new ContextParseException("Query name mistach"); + StructureItemQuery result = new StructureItemQuery(this, name); + t = tkn.next(); + if ("engine".equals(t.getValue())) { + t = tkn.next(); + if (t.getType() != QTokenType.WORD && t.getType() != QTokenType.LITERAL) + throw new ContextParseException("engine name type error"); + if ("any".equals(t.getValue())) + result.engine = null; + else + result.engine = t.getValue(); + t = tkn.next(); + } + if ("type".equals(t.getValue())) + result.returnTypeName = elementReadClassName(tkn); + else if (!"{".equals(t.getValue())) + throw new ContextParseException("{ mistach"); + StringBuilder query = new StringBuilder(); + ArrayList names = new ArrayList(); + while (!tkn.patrol().equals(QTokenType.SYMBOL, "}")) { + QToken last = t; + t = tkn.next(); + if (query.length() != 0 && (t.getType() != QTokenType.SYMBOL && last.getType() != QTokenType.SYMBOL)) + query.append(' '); + if ("#".equals(t.getValue())) { + query.append('?'); + StringBuilder inner = new StringBuilder(); + while (!"#".equals(tkn.patrol().getValue())) + inner.append(tkn.next().getValue()); + tkn.next(); + names.add(inner.toString()); + } else { + if (t.getType() == QTokenType.LITERAL) + query.append("'"); + query.append(t.getValue()); + if (t.getType() == QTokenType.LITERAL) + query.append("'"); + } + } + tkn.next(); + result.query = query.toString(); + result.names = names.toArray(new String[names.size()]); + return result; + } + + private StructureItem readPage(QTokenizer tkn) { + QToken t = tkn.next(); + + if (t == null) + throw new ContextParseException("Unexpected end of script"); + boolean regex = false; + if (t.getType() == QTokenType.SYMBOL && "~".equals(t.getValue())) { + regex = true; + t = tkn.next(); + } + if (t.getType() != QTokenType.LITERAL) + throw new ContextParseException("Invalid page url"); + String name = t.getValue(); + if ("for".equals(name)) + throw new ContextParseException("Page url mistach"); + if ("public".equals(name)) + throw new ContextParseException("Page url mistach"); + if ("private".equals(name)) + throw new ContextParseException("Page url mistach"); + if (!regex && !name.startsWith("/")) + throw new ContextParseException("Page url must start with \"/\""); + StructureItemPage result = new StructureItemPage(this, name); + result.regex = regex; + t = tkn.next(); + if ("public".equals(t.getValue())) { + result.setPublicPage(); + } else if ("private".equals(t.getValue())) { + result.setPrivatePage(); + } else { + if (!"for".equals(t.getValue())) + throw new ContextParseException("Page groups definition mistach"); + boolean done = false; + do { + t = tkn.next(); + if ("!".equals(t.getValue())) { + t = tkn.next(); + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("Page group name error"); + result.addGroup(t.getValue(), false); + } else { + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("Page group name error"); + result.addGroup(t.getValue(), true); + } + if (",".equals(tkn.patrol().getValue())) + tkn.next(); + else + done = true; + } while (!done); + } + if ("workset".equals(tkn.patrol().getValue())) { + tkn.next(); + t = tkn.next(); + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("Workset name error"); + result.setWorkset(t.getValue()); + } + t = tkn.next(); + if (!";".equals(t.getValue())) + throw new ContextParseException("Missing ; for page '" + result.url + "' definition"); + return result; + } + + private StructureItem readWorkset(QTokenizer tkn) { + QToken t = tkn.next(); + if (t == null) + throw new ContextParseException("Unexpected end of script"); + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("Invalid workset name"); + String name = t.getValue(); + if ("class".equals(name)) + throw new ContextParseException("Workset name mistach"); + StructureItemWorkset result = new StructureItemWorkset(this, name); + t = tkn.next(); + if (!"class".equals(t.getValue())) + throw new ContextParseException("class mistach"); + String className = elementReadClassName(tkn); + result.setClassName(className); + return result; + } + + private StructureItem readChangeset(QTokenizer tkn) { + QToken t = tkn.next(); + if (t == null) + throw new ContextParseException("Unexpected end of script"); + if (t.getType() != QTokenType.LITERAL) + throw new ContextParseException("Invalid changeset id literal"); + if (t.getValue().length() > 128) + throw new ContextParseException("Changeset id too long :'" + t.getValue() + "'"); + StructureItemChangeset result = new StructureItemChangeset(this, t.getValue()); + t = tkn.next(); + if ("engine".equals(t.getValue())) { + t = tkn.next(); + if (t.getType() != QTokenType.WORD && t.getType() != QTokenType.LITERAL) + throw new ContextParseException("Engine name type error"); + if ("any".equals(t.getValue())) + result.engine = null; + else + result.engine = t.getValue(); + t = tkn.next(); + } + if (!"author".equals(t.getValue())) + throw new ContextParseException("Missing author"); + t = tkn.next(); + if (t.getType() != QTokenType.LITERAL) + throw new ContextParseException("Author name type error"); + result.author = t.getValue(); + t = tkn.next(); + if ("execute".equals(t.getValue())) { + t = tkn.next(); + try { + result.executeMode = Changeset.ExecutionMode.valueOf(t.getValue().toUpperCase()); + } catch (IllegalArgumentException e) { + throw new ContextParseException("Changeset execution mode not supported"); + } + } + if (!"{".equals(t.getValue())) + throw new ContextParseException("{ mistach"); + StringBuilder query = new StringBuilder(); + while (!tkn.patrol().equals(QTokenType.SYMBOL, "}")) { + QToken last = t; + t = tkn.next(); + if (t.equals(QTokenType.SYMBOL, ";")) { + result.addQuery(query.toString()); + query.setLength(0); + } else { + if (query.length() != 0 && (t.getType() != QTokenType.SYMBOL && last.getType() != QTokenType.SYMBOL)) + query.append(' '); + if (t.getType() == QTokenType.LITERAL) + query.append("'"); + query.append(t.getValue()); + if (t.getType() == QTokenType.LITERAL) + query.append("'"); + } + } + if (query.length() > 0) + result.addQuery(query.toString()); + tkn.next(); + return result; + } + + private String elementReadClassName(QTokenizer tkn) { + String className = ""; + do { + QToken t = tkn.next(); + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("Class name error: " + t.getValue()); + className += t.getValue(); + t = tkn.next(); + if (t.getType() != QTokenType.SYMBOL) + throw new ContextParseException("Class name separator error: " + t.getValue()); + if (".".equals(t.getValue())) + className += "."; + else if ("{".equals(t.getValue()) || ";".equals(t.getValue())) + break; + else + throw new ContextParseException("Class name separator error: " + t.getValue()); + } while (true); + return className; + } + + public Object get(String name) { + if (name == null) + return null; + name = name.toLowerCase(); + if (singletons.containsKey(name)) + return singletons.get(name); + for (StructureItem i : structure) + if (name.equals(i.id)) { + Object result = i.get(); + if (i.canPreinstantiate()) { + singletons.put(i.id, result); + structure.remove(i); + } + return result; + } + return null; + } + + /** + * Retorna la query con el nombre especificado que mejor concuerde con el + * engine + * + * @param name + * @param engine + * @return + */ + public Query getQuery(String name, String engine) { + if (name == null) + throw new NiHaoException("Query name can't be null"); + name = name.toLowerCase(); + HashMap engines = queries.get(name); + if (engines == null) + new NiHaoException("Query '" + name + "' not found"); + Query result = engines.get(engine); + if (result == null && engine != null) + result = engines.get(null); + if (result == null) + result = engines.values().iterator().next(); + return result; + } + + /** + * Returns and discard all changesets + * + * @return + */ + public ArrayList getChangesets(String engine) { + QDB q = new QDB(); + HashSet ids = new LinkedHashSet(); + for (Changeset ch : changesets) { + ids.add(ch.getId()); + q.$(ch.getId()).$(ch.getEngine()).set(ch); + } + ArrayList result = new ArrayList(); + for (String id : ids) { + QDB qchange = q.$(id); + Changeset ch = qchange.$(engine).get(); + if (ch == null) + ch = qchange.$(null).get(); + if (ch == null) + ch = qchange.iterator().next().get(); + result.add(ch); + } + return result; + } + + /** + * Return true if any chagneset was defined (also if defined the bootstrap + * changeset) + * + * @return + */ + public boolean haveChangesets() { + return !changesets.isEmpty(); + } + + /** + * Retorna la página diseñada para una URL determinada + * + * @param url + * String + * @return Page + */ + public Page getPage(String url) { + try { + Page page = pages.get(url); + if (page != null) + return page; + for (Page p : regexPages) + if (url.matches(p.getUrl())) + return p; + } catch (Throwable t) { + } + return null; + } + + /** + * Return the struncture info + * + * @param name + * @return + */ + public StructureItem getStructure(String name) { + for (StructureItem i : structure) + if (name.equals(i.id)) + return i; + return null; + } + + /** + * Retorna true si el contexto no tiene nada definido + * + * @return + */ + public boolean isEmprty() { + return structure.size() == 0 && singletons.size() == 0; + } + + /** + * Realiza el commit de sus estructuras y preinstancia singletons + */ + public void commit() { + singletons = new HashMap(); + for (StructureItem i : fullstructure) + i.commit(); + ArrayList toKill = new ArrayList(); + for (StructureItem i : structure) + if (i instanceof StructureItemQuery) { + Query q = (Query) i.get(); + HashMap engines = queries.get(q.getId()); + if (engines == null) { + engines = new HashMap(); + queries.put(q.getId(), engines); + } + if (engines.get(q.getEngine()) != null) + throw new NiHaoException("Query '" + q.getId() + " duplicated for engine " + Conversor.nvl(q.getEngine(), "any")); + engines.put(q.getEngine(), q); + toKill.add(i); + } else if (i instanceof StructureItemPage) { + Page p = (Page) i.get(); + if (p.isRegex()) + regexPages.add(p); + else + pages.put(p.getUrl(), p); + toKill.add(i); + } else if (i instanceof StructureItemChangeset) { + changesets.add((Changeset) i.get()); + toKill.add(i); + } else if (i.canPreinstantiate()) { + singletons.put(i.id, i.get()); + toKill.add(i); + } + for (StructureItem i : toKill) + structure.remove(i); + fullstructure = null; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (StructureItem i : structure) { + sb.append(i.toString()); + sb.append('\n'); + } + if (singletons != null) + for (String k : singletons.keySet()) { + sb.append(k); + sb.append(" \n"); + } + for (HashMap hm : queries.values()) + for (Entry e : hm.entrySet()) { + sb.append(e.getValue().getId()); + sb.append(" \n"); + } + return sb.toString(); + } +} diff --git a/NiHao/src/nihao/context/ContextParseException.java b/NiHao/src/nihao/context/ContextParseException.java new file mode 100644 index 0000000..a6aa550 --- /dev/null +++ b/NiHao/src/nihao/context/ContextParseException.java @@ -0,0 +1,16 @@ +package nihao.context; + +public class ContextParseException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public ContextParseException() { + } + + public ContextParseException(String message) { + super(message); + } + + public ContextParseException(String message, Throwable t) { + super(message, t); + } +} diff --git a/NiHao/src/nihao/context/FileContext.java b/NiHao/src/nihao/context/FileContext.java new file mode 100644 index 0000000..341ee29 --- /dev/null +++ b/NiHao/src/nihao/context/FileContext.java @@ -0,0 +1,27 @@ +package nihao.context; + +import java.io.InputStream; + +import nihao.NiHaoException; +import nihao.util.Conversor; +import nihao.util.Resources; + +public class FileContext extends Context { + + public FileContext(String path) { + InputStream input = Resources.getResourceAsStream(path); + if (input==null) + throw new NiHaoException("Can't open context file: "+path); + read(input); + } + + public FileContext(InputStream input) { + if (input==null) + throw new IllegalArgumentException("null InputStream"); + read(input); + } + + private void read(InputStream input) { + parse(Conversor.readToString(input)); + } +} diff --git a/NiHao/src/nihao/context/StringContext.java b/NiHao/src/nihao/context/StringContext.java new file mode 100644 index 0000000..afca278 --- /dev/null +++ b/NiHao/src/nihao/context/StringContext.java @@ -0,0 +1,8 @@ +package nihao.context; + +public class StringContext extends Context{ + + public StringContext(String text){ + parse(text); + } +} diff --git a/NiHao/src/nihao/context/StructureItem.java b/NiHao/src/nihao/context/StructureItem.java new file mode 100644 index 0000000..b1e468b --- /dev/null +++ b/NiHao/src/nihao/context/StructureItem.java @@ -0,0 +1,60 @@ +package nihao.context; + +public abstract class StructureItem { + protected String id; + boolean singleton = true; + Context owner; + + public StructureItem(Context owner, String id) { + this.owner = owner; + if (id != null) + id = id.toLowerCase(); + this.id = id; + owner.fullstructure.add(this); + } + + /** + * Retorna el ID de la estructura + * + * @return + */ + public String getId() { + return id; + } + + /** + * True si el elemento es un singleton + * + * @return + */ + public boolean isSingleton() { + return singleton; + } + + /** + * Retorna la instancia del objeto reflejado en la estructura + * + * @return + */ + public abstract Object get(); + + /** + * Retorna true si el objeto es un singleton por completo (incluidas sus + * dependencias) + * + * @return + */ + public abstract boolean canPreinstantiate(); + + /** + * Retorna la clase de la instancia reflejada por la estructura + * + * @return + */ + public abstract Class getStructureClass(); + + /** + * Realiza ajustes de finalizaci�n, en las estructuras + */ + public abstract void commit(); +} diff --git a/NiHao/src/nihao/context/StructureItemArray.java b/NiHao/src/nihao/context/StructureItemArray.java new file mode 100644 index 0000000..34290c5 --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemArray.java @@ -0,0 +1,64 @@ +package nihao.context; + +import java.lang.reflect.Array; + +import nihao.util.reflection.Reflector; + +public class StructureItemArray extends StructureItem { + private String typeName; + private Class type; + private StructureItem[] items; + + public StructureItemArray(Context owner, String id, String typeName, StructureItem[] items) { + super(owner, id); + this.typeName = typeName; + this.type = null; + this.items = items; + } + + @Override + public Object get() { + Object result = Array.newInstance(type, items.length); + for (int i = 0; i < items.length; i++) + Array.set(result, i, items[i].get()); + return result; + } + + @Override + public Class getStructureClass() { + return Array.newInstance(type, 0).getClass(); + } + + @Override + public boolean canPreinstantiate() { + for (StructureItem i : items) + if (!i.canPreinstantiate()) + return false; + return true; + } + + @Override + public void commit() { + type = Reflector.getClass(typeName); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (!singleton) + sb.append("multi "); + sb.append("array "); + if (id != null) { + sb.append(id); + sb.append(' '); + } + sb.append("{\n"); + for (StructureItem i : items) { + sb.append(' '); + sb.append(i); + sb.append('\n'); + } + sb.append("}"); + return sb.toString(); + } +} diff --git a/NiHao/src/nihao/context/StructureItemChangeset.java b/NiHao/src/nihao/context/StructureItemChangeset.java new file mode 100644 index 0000000..553c1c5 --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemChangeset.java @@ -0,0 +1,55 @@ +package nihao.context; + +import java.util.ArrayList; + +import nihao.db.Changeset; +import nihao.util.Conversor; +import nihao.util.Hasher; + +public class StructureItemChangeset extends StructureItem { + String engine; + String author; + private ArrayList queries = new ArrayList(); + private String hash; + Changeset.ExecutionMode executeMode; + + public StructureItemChangeset(Context owner, String id) { + super(owner, id); + } + + public void addQuery(String q) { + if (q == null || q.length() == 0) + return; + queries.add(q); + } + + @Override + public String toString() { + return "changeset: " + id; + } + + @Override + public Object get() { + return new Changeset(id, engine, author, queries.toArray(new String[queries.size()]), hash, executeMode); + } + + @Override + public boolean canPreinstantiate() { + return true; + } + + @Override + public Class getStructureClass() { + return Changeset.class; + } + + @Override + public void commit() { + StringBuilder sb = new StringBuilder(); + for (String s : queries) { + sb.append(s); + sb.append(';'); + } + hash = Conversor.bytesToHex(Hasher.SHA1(Conversor.utf8ToBytes(sb.toString()))); + } +} diff --git a/NiHao/src/nihao/context/StructureItemList.java b/NiHao/src/nihao/context/StructureItemList.java new file mode 100644 index 0000000..2c05fc4 --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemList.java @@ -0,0 +1,60 @@ +package nihao.context; + +import java.util.ArrayList; + +public class StructureItemList extends StructureItem { + private ArrayList items = new ArrayList(); + + public StructureItemList(Context owner, String id) { + super(owner, id); + } + + public void add(StructureItem i) { + items.add(i); + } + + @Override + public Object get() { + ArrayList result = new ArrayList(); + for (StructureItem i : items) + result.add(i.get()); + return result; + } + + @Override + public Class getStructureClass() { + return ArrayList.class; + } + + @Override + public boolean canPreinstantiate() { + for (StructureItem i : items) + if (!i.canPreinstantiate()) + return false; + return true; + } + + @Override + public void commit() { + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (!singleton) + sb.append("multi "); + sb.append("list "); + if (id != null) { + sb.append(id); + sb.append(' '); + } + sb.append("{\n"); + for (StructureItem i : items) { + sb.append(' '); + sb.append(i); + sb.append('\n'); + } + sb.append("}"); + return sb.toString(); + } +} diff --git a/NiHao/src/nihao/context/StructureItemMap.java b/NiHao/src/nihao/context/StructureItemMap.java new file mode 100644 index 0000000..4d7bbeb --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemMap.java @@ -0,0 +1,65 @@ +package nihao.context; + +import java.util.HashMap; +import java.util.Map.Entry; + +public class StructureItemMap extends StructureItem { + private HashMap map = new HashMap(); + + public StructureItemMap(Context owner, String id) { + super(owner, id); + } + + @Override + public HashMap get() { + HashMap result = new HashMap(); + for (Entry e : map.entrySet()) + result.put(e.getKey().get(), e.getValue().get()); + return result; + } + + public void putMap(StructureItem k, StructureItem v) { + map.put(k, v); + } + + @Override + public Class getStructureClass() { + return HashMap.class; + } + + @Override + public boolean canPreinstantiate() { + for (StructureItem m : map.keySet()) + if (!m.canPreinstantiate()) + return false; + else if (!map.get(m).canPreinstantiate()) + return false; + return true; + } + + @Override + public void commit() { + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (!singleton) + sb.append("multi "); + sb.append("map "); + if (id != null) { + sb.append(id); + sb.append(' '); + } + sb.append("{\n"); + for (StructureItem k : map.keySet()) { + sb.append(" {"); + sb.append(k); + sb.append(','); + sb.append(map.get(k)); + sb.append("}\n"); + } + sb.append("}"); + return sb.toString(); + } +} diff --git a/NiHao/src/nihao/context/StructureItemNull.java b/NiHao/src/nihao/context/StructureItemNull.java new file mode 100644 index 0000000..aa303be --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemNull.java @@ -0,0 +1,31 @@ +package nihao.context; + +public class StructureItemNull extends StructureItem { + public StructureItemNull(Context owner) { + super(owner, null); + } + + @Override + public Object get() { + return null; + } + + @Override + public Class getStructureClass() { + return null; + } + + @Override + public boolean canPreinstantiate() { + return true; + } + + @Override + public void commit() { + } + + @Override + public String toString() { + return "null"; + } +} diff --git a/NiHao/src/nihao/context/StructureItemObject.java b/NiHao/src/nihao/context/StructureItemObject.java new file mode 100644 index 0000000..326447e --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemObject.java @@ -0,0 +1,152 @@ +package nihao.context; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; + +import nihao.util.reflection.Reflector; + +public class StructureItemObject extends StructureItem { + private ArrayList constructor = new ArrayList(); + private String clsName; + private Class cls; + private HashMap flds = new HashMap(); + + public StructureItemObject(Context owner, String id) { + super(owner, id); + } + + public void addConstructor(StructureItem item) { + constructor.add(item); + } + + public void setObjectClass(String clsName) { + this.clsName = clsName; + } + + public void setParam(String name, StructureItem item) { + flds.put(name, item); + } + + @Override + public Object get() { + Class[] ctorpt = new Class[constructor.size()]; + for (int i = 0; i < ctorpt.length; i++) + ctorpt[i] = constructor.get(i).getStructureClass(); + Constructor ctor = Reflector.getCompatibleConstructor(cls, ctorpt); + if (ctor == null) + throw new ContextParseException("Can't find constructor for class " + cls.getName() + " defined in " + id); + Object[] ctorpv = new Object[constructor.size()]; + for (int i = 0; i < ctorpv.length; i++) + ctorpv[i] = constructor.get(i).get(); + Object result; + try { + result = ctor.newInstance(ctorpv); + } catch (IllegalArgumentException e) { + throw new ContextParseException("Error instantiating class " + cls.getName() + " defined in " + id, e); + } catch (InstantiationException e) { + throw new ContextParseException("Error instantiating class " + cls.getName() + " defined in " + id, e); + } catch (IllegalAccessException e) { + throw new ContextParseException("Error instantiating class " + cls.getName() + " defined in " + id, e); + } catch (InvocationTargetException e) { + throw new ContextParseException("Error instantiating class " + cls.getName() + " defined in " + id, e); + } + for (String name : flds.keySet()) { + StructureItem item = flds.get(name); + Method m = Reflector.getSetter(cls, name); + boolean setted = m != null; + Object v = item.get(); + if (setted) + try { + m.invoke(result, v); + } catch (IllegalArgumentException e) { + setted = false; + } catch (IllegalAccessException e) { + setted = false; + } catch (InvocationTargetException e) { + setted = false; + } + Class c = cls; + while (!setted && c != null) { + for (Field f : c.getDeclaredFields()) + if (f.getName().equals(name)) + try { + f.setAccessible(true); + f.set(result, v); + setted = true; + break; + } catch (IllegalAccessException e) { + throw new ContextParseException("Error setting property '" + name + "' of class " + cls.getName() + " defined in " + id, e); + } + c = c.getSuperclass(); + } + if (!setted) + throw new ContextParseException("Can't find property '" + name + "' or setter in class " + cls.getName() + " defined in " + id); + } + return result; + } + + @Override + public Class getStructureClass() { + return cls; + } + + @Override + public boolean canPreinstantiate() { + if (!singleton) + return false; + for (StructureItem i : constructor) + if (!i.canPreinstantiate()) + return false; + for (String m : flds.keySet()) + if (!flds.get(m).canPreinstantiate()) + return false; + return true; + } + + @Override + public void commit() { + cls = Reflector.getClass(clsName); + clsName = null; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (!singleton) + sb.append("multi "); + sb.append("object "); + if (id != null) { + sb.append(id); + sb.append(' '); + } + sb.append("class "); + if (cls == null) + sb.append(""); + else + sb.append(cls.getName()); + sb.append('{'); + if (constructor.size() > 0) { + sb.append('('); + for (int i = 0; i < constructor.size(); i++) { + if (i != 0) + sb.append(','); + sb.append(constructor.get(i).toString()); + } + sb.append(")\n"); + } else + sb.append('\n'); + for (String m : flds.keySet()) { + sb.append(' '); + sb.append(m); + sb.append('='); + sb.append(flds.get(m).toString()); + sb.append('\n'); + } + sb.append("}"); + return sb.toString(); + } +} diff --git a/NiHao/src/nihao/context/StructureItemPage.java b/NiHao/src/nihao/context/StructureItemPage.java new file mode 100644 index 0000000..3da17ca --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemPage.java @@ -0,0 +1,107 @@ +package nihao.context; + +import java.util.ArrayList; + +import nihao.Page; +import nihao.types.PageSecuityType; + +/** + * Una definición de página + * @author XWolf Override + * + */ +public class StructureItemPage extends StructureItem { + String url; + private ArrayList groupsA = new ArrayList(); + private ArrayList groupsD = new ArrayList(); + private String[] grA, grD; + private String workset; + private PageSecuityType security = PageSecuityType.BYGROUP; + boolean regex; + + // private StructureItem worksetStruct; + + public StructureItemPage(Context owner, String id) { + super(owner, id); + url = id; + } + + @Override + public Object get() { + Page result = new Page(url, security); + // if (workset != null) + // if (worksetStruct == null) + // result.setWorksetStructure(worksetStruct); + // else + // result.setWorksetSingleton(owner.get(workset)); + result.setGroupAllow(grA); + result.setGroupDeny(grD); + result.setWorkset(workset); + result.setRegex(regex); + return result; + } + + @Override + public Class getStructureClass() { + return Page.class; + } + + @Override + public boolean canPreinstantiate() { + return true; + } + + @Override + public void commit() { + if (groupsA.size() > 0) + grA = groupsA.toArray(new String[groupsA.size()]); + if (groupsD.size() > 0) + grD = groupsD.toArray(new String[groupsD.size()]); + groupsA = null; + groupsD = null; + // if (workset != null) { + // worksetStruct = owner.getStructure(workset); + // if (worksetStruct == null) + // throw new ContextParseException("Page '" + url + "' workset '" + + // workset + "' not found"); + // if (!(worksetStruct instanceof StructureItemWorkset) && + // !(worksetStruct instanceof StructureItemObject)) + // throw new ContextParseException("Page '" + url + "' workset '" + + // workset + "' not defined as a workset or object"); + // if (worksetStruct.isSingleton()) + // worksetStruct = null; + // } + } + + @Override + public String toString() { + return "page " + url; + } + + public void addGroup(String name, boolean allow) { + if (allow) + groupsA.add(name.substring(1)); + else + groupsD.add(name); + } + + public void setWorkset(String workset) { + this.workset = workset; + } + + public void setPublicPage() { + this.security = PageSecuityType.PUBLIC; + } + + public void setPrivatePage() { + this.security = PageSecuityType.PRIVATE; + } + + /** + * Treat URL as a regex syntax + * @return + */ + public boolean isRegex(){ + return regex; + } +} diff --git a/NiHao/src/nihao/context/StructureItemProvider.java b/NiHao/src/nihao/context/StructureItemProvider.java new file mode 100644 index 0000000..b175590 --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemProvider.java @@ -0,0 +1,44 @@ +package nihao.context; + +import nihao.ProviderInfo; + +public class StructureItemProvider extends StructureItem { + private String def; + private String[] aux; + + public StructureItemProvider(Context owner, String id) { + super(owner, id); + } + + public void setDefault(String def) { + this.def = def; + } + + public void setAuxiliar(String[] aux) { + this.aux = aux; + } + + @Override + public Object get() { + return new ProviderInfo(id, def, aux); + } + + @Override + public Class getStructureClass() { + return null; + } + + @Override + public boolean canPreinstantiate() { + return true; + } + + @Override + public void commit() { + } + + @Override + public String toString() { + return "provider "+id+" default "+def+"{\n"+"}"; + } +} diff --git a/NiHao/src/nihao/context/StructureItemQuery.java b/NiHao/src/nihao/context/StructureItemQuery.java new file mode 100644 index 0000000..cea76c3 --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemQuery.java @@ -0,0 +1,44 @@ +package nihao.context; + +import nihao.db.Query; +import nihao.util.reflection.Reflector; + +public class StructureItemQuery extends StructureItem { + String engine; + String returnTypeName; + private Class returnType; + String query; + String[] names; + + public StructureItemQuery(Context owner, String id) { + super(owner, id); + } + + @Override + public Object get() { + return new Query(id, engine, returnType, query, names); + } + + @Override + public Class getStructureClass() { + return Query.class; + } + + @Override + public boolean canPreinstantiate() { + return true; + } + + @Override + public void commit() { + if (returnTypeName != null) + returnType = Reflector.getClass(returnTypeName); + else + returnType = null; + } + + @Override + public String toString() { + return "query"; + } +} diff --git a/NiHao/src/nihao/context/StructureItemReference.java b/NiHao/src/nihao/context/StructureItemReference.java new file mode 100644 index 0000000..d8f5c8d --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemReference.java @@ -0,0 +1,44 @@ +package nihao.context; + +public class StructureItemReference extends StructureItem { + private String linkto; + private StructureItem link; + + public StructureItemReference(Context owner, String id, String linkto) { + super(owner, id); + this.linkto = linkto.toLowerCase(); + } + + public StructureItem getLink() { + if (link == null) { + link = owner.getStructure(linkto); + if (link == null) + throw new ContextParseException("Cant find reference to " + linkto); + } + return link; + } + + @Override + public Object get() { + return getLink().get(); + } + + @Override + public Class getStructureClass() { + return getLink().getStructureClass(); + } + + @Override + public boolean canPreinstantiate() { + return getLink().canPreinstantiate(); + } + + @Override + public void commit() { + } + + @Override + public String toString() { + return "@" + linkto; + } +} diff --git a/NiHao/src/nihao/context/StructureItemValue.java b/NiHao/src/nihao/context/StructureItemValue.java new file mode 100644 index 0000000..db61631 --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemValue.java @@ -0,0 +1,68 @@ +package nihao.context; + +public class StructureItemValue extends StructureItem { + public enum Type { + STRING, NUMBER, BOOL + } + + private Object value; + + public StructureItemValue(Context owner, String id, String value, Type type) { + super(owner, id); + switch (type) { + case STRING: + this.value = value; + break; + case NUMBER: + if (value.contains(".")) + this.value = Double.parseDouble(value); + else + this.value = Integer.parseInt(value); + break; + case BOOL: + if ("true".equalsIgnoreCase(value)) + this.value = true; + else if ("false".equalsIgnoreCase(value)) + this.value = false; + else + throw new ContextParseException("unknown boolean ''" + value + "''"); + } + } + + @Override + public Object get() { + return value; + } + + @Override + public Class getStructureClass() { + if (value == null) + return null; + return value.getClass(); + } + + @Override + public boolean canPreinstantiate() { + return true; + } + + @Override + public void commit() { + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (id != null) { + sb.append("val "); + sb.append(id); + sb.append(' '); + } + if (value instanceof String) + sb.append('"'); + sb.append(value.toString()); + if (value instanceof String) + sb.append('"'); + return sb.toString(); + } +} diff --git a/NiHao/src/nihao/context/StructureItemWorkset.java b/NiHao/src/nihao/context/StructureItemWorkset.java new file mode 100644 index 0000000..76875a1 --- /dev/null +++ b/NiHao/src/nihao/context/StructureItemWorkset.java @@ -0,0 +1,54 @@ +package nihao.context; + +import nihao.NiHaoException; +import nihao.Workset; +import nihao.util.reflection.Reflector; + +public class StructureItemWorkset extends StructureItem { + private String className; + private Class cls; + + public StructureItemWorkset(Context owner, String id) { + super(owner, id); + } + + @Override + public Object get() { + try { + return cls.newInstance(); + } catch (IllegalArgumentException e) { + throw new ContextParseException("Error instantiating class " + cls.getName() + " defined in " + id, e); + } catch (InstantiationException e) { + throw new ContextParseException("Error instantiating class " + cls.getName() + " defined in " + id, e); + } catch (IllegalAccessException e) { + throw new ContextParseException("Error instantiating class " + cls.getName() + " defined in " + id, e); + } + } + + @Override + public Class getStructureClass() { + return cls; + } + + @Override + public boolean canPreinstantiate() { + return false; // never for worksets + } + + @Override + public void commit() { + cls = Reflector.getClass(className); + if (!Workset.class.isAssignableFrom(cls)) + throw new NiHaoException("Workset '" + id + "' class '" + className + "' must extend Workset"); + className = null; + } + + public void setClassName(String className) { + this.className = className; + } + + @Override + public String toString() { + return "workset " + id + " class " + className + ";"; + } +} diff --git a/NiHao/src/nihao/db/Changeset.java b/NiHao/src/nihao/db/Changeset.java new file mode 100644 index 0000000..d70f137 --- /dev/null +++ b/NiHao/src/nihao/db/Changeset.java @@ -0,0 +1,66 @@ +package nihao.db; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import nihao.log.LogProvider; +import nihao.util.Conversor; + +public class Changeset { + public enum ExecutionMode { + ONCE, ALWAYS, ONCHANGE + } + + private String id; + private String engine; + private String author; + private String[] query; + private String hash; + private ExecutionMode executionMode; + + public Changeset(String id, String engine, String author, String[] query, String hash, ExecutionMode executionMode) { + this.id = id; + this.engine = engine; + this.author = author; + this.query = query; + this.hash = hash; + this.executionMode = executionMode; + } + + void execute(Connection con) throws SQLException { + LogProvider.getProvider().info("Changeset \"" + id + "\" for " + Conversor.nvl(engine, "any")); + Statement st = con.createStatement(); + for (String query : query) + if (query.length() > 0) { + LogProvider.getProvider().debug(query); + st.execute(query); + } + st.close(); + } + + public String getId() { + return id; + } + + public String getEngine() { + return engine; + } + + public String getAuthor() { + return author; + } + + public String getHash() { + return hash; + } + + public ExecutionMode getExecutionMode() { + return executionMode; + } + + @Override + public String toString() { + return "Changeset '" + id + "'"; + } +} diff --git a/NiHao/src/nihao/db/ChangesetDataManager.java b/NiHao/src/nihao/db/ChangesetDataManager.java new file mode 100644 index 0000000..65c72c5 --- /dev/null +++ b/NiHao/src/nihao/db/ChangesetDataManager.java @@ -0,0 +1,67 @@ +package nihao.db; + +import java.sql.SQLException; +import java.sql.Timestamp; + +import nihao.NiHaoException; +import nihao.context.Context; +import nihao.db.Changeset.ExecutionMode; +import nihao.dto.ChangesetDTO; + +public class ChangesetDataManager extends DataManager { + public ChangesetDataManager() { + super(); + } + + public ChangesetDataManager(String connection) { + super(connection); + } + + public ChangesetDataManager(DataSourceProvider connection) { + super(connection); + } + + public void runChagesets(Context ctx) { + for (Changeset ch : ctx.getChangesets(getEngine())) + runChangeset(ch); + } + + private void runChangeset(Changeset ch) { + DataTransaction trans = beginTransaction(); + try { + ChangesetDTO change; + try { + change = trans.selectAs("nhaochse_getChange", ch.getId(), ChangesetDTO.class); + } catch (SQLException e) { + if ("bootstrap".equals(ch.getId())) + change = null; + else + throw e; + } + if (change == null) { + ch.execute(con); + change = new ChangesetDTO(); + change.setAuthor(ch.getAuthor()); + change.setHash(ch.getHash()); + change.setId(ch.getId()); + change.setExecuted(new Timestamp((new java.util.Date()).getTime())); + execute("nhaochse_insertChange", change); + } else { + boolean changed = !ch.getHash().equals(change.getHash()); + if (ch.getExecutionMode() == ExecutionMode.ALWAYS || (changed) && ch.getExecutionMode() == ExecutionMode.ONCHANGE) { + ch.execute(con); + change.setExecuted(new Timestamp((new java.util.Date()).getTime())); + execute("nhaochse_updateChange", change); + } else if (changed) + throw new NiHaoException("Changeset '" + ch.getId() + "' executed with hash " + change.getHash() + " and now the hash is " + ch.getHash()); + } + + } catch (Throwable t) { + rollbackTransaction(trans); + System.err.println(); + if (t instanceof RuntimeException) + throw (RuntimeException) t; + throw new NiHaoException(t); + } + } +} diff --git a/NiHao/src/nihao/db/DataManager.java b/NiHao/src/nihao/db/DataManager.java new file mode 100644 index 0000000..31269a1 --- /dev/null +++ b/NiHao/src/nihao/db/DataManager.java @@ -0,0 +1,201 @@ +package nihao.db; + +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; + +import nihao.NiHaoException; + +/*** + * Clase de acceso a datos + * + * @author ivan.dominguez + * + */ +public class DataManager { + private DataSourceProvider data; + Connection con; + + /*** + * Instancia de acceso a BBDD + */ + public DataManager() { + data = DataSourceProvider.getProvider(); + init(); + } + + public DataManager(String connection) { + if (connection == null) + data = DataSourceProvider.getProvider(); + else + data = DataSourceProvider.getProvider(connection); + init(); + } + + public DataManager(DataSourceProvider provider) { + data = provider; + if (data == null) + data = DataSourceProvider.getProvider(); + init(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + try { + con.close(); + } catch (Throwable t) { + } + } + + private void init() { + con = data.getConnection(); + try { + con.setAutoCommit(false); + } catch (SQLException e) { + throw new NiHaoException(e); + } + } + + protected DataTransaction beginTransaction() { + return new DataTransaction(con, data.engine); + } + + protected void rollbackTransaction(DataTransaction trans) { + try { + trans.rollback(); + } catch (SQLException | NullPointerException e) { + } + } + + protected void commitTransaction(DataTransaction trans) { + try { + trans.commit(); + } catch (SQLException e) { + throw new NiHaoException(e); + } + } + + /** + * Ejecuta la Query y castea el resultado, si la clase de retorno no es un + * array se retorna solo el primer elemento, si lo es se retorna el array + * del tipo correspondiente. + * + * @param + * Tipo retornado + * @param queryname + * Nombre de query + * @param param + * Parametros de entrada, objeto, hashmap o valor. + * @param resultClass + * Tipo (usar "[].class" para retornar arrays) + * @return + */ + public T selectAs(String queryname, Object param, Class resultClass) { + DataTransaction trans = beginTransaction(); + try { + T result = trans.selectAs(queryname, param, resultClass); + commitTransaction(trans); + return result; + } catch (Throwable t) { + rollbackTransaction(trans); + if (t instanceof RuntimeException) + throw (RuntimeException) t; + throw new NiHaoException(t); + } + } + + /** + * Ejecuta la query y castea, Los datos siempre se retornan en forma de + * Array + * + * @param + * @param queryname + * Nombre de query + * @param param + * Parametros de entrada, objeto, hashmap o valor. + * @param limit + * Limite máximo de elementos de retorno + * @return + */ + public T[] selectAs(String queryname, Object param, int limit) { + DataTransaction trans = beginTransaction(); + try { + T[] result = trans.selectAs(queryname, param, limit); + commitTransaction(trans); + return result; + } catch (Throwable t) { + rollbackTransaction(trans); + if (t instanceof RuntimeException) + throw (RuntimeException) t; + throw new NiHaoException(t); + } + } + + /** + * Executes the query isolated in one transaction + * + * @param queryname + * String name of registered query + * @param param + * Object any opbject for params + * @return int number of changes on database + */ + public int execute(String queryname, Object param) { + DataTransaction trans = beginTransaction(); + try { + int result = trans.execute(queryname, param); + commitTransaction(trans); + return result; + } catch (Throwable t) { + rollbackTransaction(trans); + if (t instanceof RuntimeException) + throw (RuntimeException) t; + throw new NiHaoException(t); + } + } + + /** + * Copies the object getters values to a HasMap + * + * @param o + * Object source + * @param hm + * HashMap destination + * @param fld + * Strings. if any defined, copies only the defined getter names, + * if not, get all data + */ + public static void joinObjectToMap(Object o, HashMap hm, String... fld) { + Class cls = o.getClass(); + if (fld == null || fld.length == 0) { + Method[] allm = cls.getMethods(); + for (Method m : allm) { + String mname = m.getName(); + if (mname.startsWith("get") && (!"getClass".equals(mname))) { + mname = mname.substring(3, 4).toLowerCase().concat(mname.substring(4)); + try { + hm.put(mname, m.invoke(o)); + } catch (Exception e) { + } + } + } + } else + for (String s : fld) + try { + Method m = cls.getMethod(s); + hm.put(s, m.invoke(o)); + } catch (Exception e) { + } + } + + /** + * Return the engine associated to the DataManager + * + * @return + */ + public String getEngine() { + return data.engine; + } +} diff --git a/NiHao/src/nihao/db/DataSourceProvider.java b/NiHao/src/nihao/db/DataSourceProvider.java new file mode 100644 index 0000000..a9cf678 --- /dev/null +++ b/NiHao/src/nihao/db/DataSourceProvider.java @@ -0,0 +1,31 @@ +package nihao.db; + +import java.sql.Connection; + +import nihao.Provider; +import nihao.ProviderInfo; +import nihao.NiHao; + +public abstract class DataSourceProvider extends Provider{ + private static ProviderInfo info=ProviderInfo.get(DataSourceProvider.class); + + public static DataSourceProvider getProvider(){ + return instanceAs(info, DataSourceProvider.class); + } + + public static DataSourceProvider getProvider(String name){ + return NiHao.getAs(name, DataSourceProvider.class); + } + + String engine; + + public abstract Connection getConnection(); + + /** + * retorna el tipo de engine del proveedor + * @return + */ + public String getEngine() { + return engine; + } +} diff --git a/NiHao/src/nihao/db/DataSourceProviderJDBC.java b/NiHao/src/nihao/db/DataSourceProviderJDBC.java new file mode 100644 index 0000000..ff27eaf --- /dev/null +++ b/NiHao/src/nihao/db/DataSourceProviderJDBC.java @@ -0,0 +1,45 @@ +package nihao.db; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import nihao.NiHaoException; + +public class DataSourceProviderJDBC extends DataSourceProvider { + private String connectionString; + private String driver; + private String user; + private String password; + + public DataSourceProviderJDBC(String connectionString) { + this.connectionString = connectionString; + } + + public void setUser(String user) { + this.user = user; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setDriver(String driver) { + this.driver = driver; + } + + @Override + public Connection getConnection() { + if (driver != null) + try { + Class.forName(driver); + } catch (ClassNotFoundException e) { + throw new NiHaoException("Can't find driver class " + driver, e); + } + try { + return DriverManager.getConnection(connectionString, user, password); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/NiHao/src/nihao/db/DataSourceProviderJNDI.java b/NiHao/src/nihao/db/DataSourceProviderJNDI.java new file mode 100644 index 0000000..9c6e5b2 --- /dev/null +++ b/NiHao/src/nihao/db/DataSourceProviderJNDI.java @@ -0,0 +1,46 @@ +package nihao.db; + +import java.sql.Connection; +import java.sql.SQLException; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; + +import nihao.NiHaoException; + +public class DataSourceProviderJNDI extends DataSourceProvider { + private String jndiConnectionName; + + public DataSourceProviderJNDI(String jndiConnectionName) { + this.jndiConnectionName = jndiConnectionName; + } + + public void setJndiConnectionName(String jndiConnectionName) { + this.jndiConnectionName = jndiConnectionName; + } + + @Override + public Connection getConnection() { + Context initialContext; + try { + initialContext = new InitialContext(); + } catch (NamingException e) { + throw new RuntimeException(e); + } + DataSource datasource = null; + try { + datasource = (DataSource) initialContext.lookup(jndiConnectionName); + } catch (NamingException e) { + throw new RuntimeException(e); + } + if (datasource == null) + throw new NiHaoException("Can't get datasource"); + try { + return datasource.getConnection(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/NiHao/src/nihao/db/DataTransaction.java b/NiHao/src/nihao/db/DataTransaction.java new file mode 100644 index 0000000..c2a4f6d --- /dev/null +++ b/NiHao/src/nihao/db/DataTransaction.java @@ -0,0 +1,91 @@ +package nihao.db; + +import java.lang.reflect.Array; +import java.sql.Connection; +import java.sql.SQLException; + +import nihao.NiHao; + +/** + * Gestor de transacciones + * + * @author ivan.dominguez + * + */ +public class DataTransaction { + private Connection con; + private String engine; + + DataTransaction(Connection con, String engine) { + this.con = con; + this.engine = engine; + } + + @Override + protected void finalize() throws Throwable { + rollback(); + } + + public void commit() throws SQLException { + con.commit(); + con = null; + } + + public void rollback() throws SQLException { + con.rollback(); + con = null; + } + + /** + * Ejecuta la Query y castea el resultado, si la clase de retorno no es un + * array se retorna solo el primer elemento, si lo es se retorna el array del tipo correspondiente + * + * @param Tipo (usar "[].class" para retornar arrays) + * @param queryname Nombre de query + * @param param Parametros de entrada, objeto, hashmap o valor. + * @param cls Clase de tipo T + * @return + * @throws SQLException + */ + public T selectAs(String queryname, Object param, Class cls) throws SQLException { + Query q = NiHao.getQuery(queryname, engine); + Object r; + if (cls.isArray()) + r = q.select(con, param); + else{ + Object result=q.select(con, param, 1); + if (Array.getLength(result)>0) + r = Array.get(q.select(con, param, 1), 0); + else + r=null; + } + return cls.cast(r); + } + + /** + * Ejecuta la query y castea, Los datos siempre se retornan en forma de Array + * @param + * @param queryname Nombre de query + * @param param Parametros de entrada, objeto, hashmap o valor. + * @param limit Limite máximo de retornos + * @return + * @throws SQLException + */ + @SuppressWarnings("unchecked") + public T[] selectAs(String queryname, Object param, int limit) throws SQLException { + Query q = NiHao.getQuery(queryname, engine); + return (T[]) q.select(con, param, limit); + } + + /** + * Executes the query in non select mode (for inserts or updates) + * @param queryname + * @param param + * @return int number of affected rows + * @throws SQLException + */ + public int execute(String queryname,Object param) throws SQLException { + Query q = NiHao.getQuery(queryname, engine); + return q.execute(con, param); + } +} diff --git a/NiHao/src/nihao/db/IQueryParamElement.java b/NiHao/src/nihao/db/IQueryParamElement.java new file mode 100644 index 0000000..4d1448e --- /dev/null +++ b/NiHao/src/nihao/db/IQueryParamElement.java @@ -0,0 +1,7 @@ +package nihao.db; + +public interface IQueryParamElement { + + public QueryParamValue evaluate(Object o); + +} diff --git a/NiHao/src/nihao/db/Query.java b/NiHao/src/nihao/db/Query.java new file mode 100644 index 0000000..0dcaa6c --- /dev/null +++ b/NiHao/src/nihao/db/Query.java @@ -0,0 +1,228 @@ +package nihao.db; + +import java.io.InputStream; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.net.URL; +import java.sql.Blob; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; + +import nihao.NiHaoException; +import nihao.util.net.Url; +import nihao.util.reflection.Reflector; + +public class Query { + private String engine; + private String id; + private Class returnType; + private String query; + private QueryParam[] params; + + public Query(String id, String engine, Class returnType, String query, String[] params) { + if (returnType !=null && returnType.isPrimitive()) + returnType = Reflector.getPrimitiveImplementationClass(returnType); + this.id = id; + this.engine = engine; + this.returnType = returnType; + this.query = query; + this.params = QueryParam.parse(params); + } + + public PreparedStatement buildStatement(Connection con, Object param) throws SQLException { + PreparedStatement q = con.prepareStatement(query); + if (params.length > 0) { + if (param == null) + throw new NiHaoException("Can't use null param on parametrized query."); + for (int i = 0; i < params.length; i++) + setParam(i + 1, q, params[i].evaluate(param)); + } + return q; + } + + public void setParam(int index, PreparedStatement q, QueryParamValue v) throws SQLException { + if (v == null) + q.setObject(index, null, Types.NULL); + q.setObject(index, v.value, v.sqlType); + } + + public Object select(Connection con, Object param) throws SQLException { + return select(con, param, -1); + } + + @SuppressWarnings("unchecked") + public Object select(Connection con, Object param, int limit) throws SQLException { + PreparedStatement s = buildStatement(con, param); + ResultSet rs = s.executeQuery(); + ResultSetMetaData rsmd = rs.getMetaData(); + int numberOfColumns = rsmd.getColumnCount(); + ArrayList l = new ArrayList(); + if (Reflector.canBePrimitive(returnType) || returnType == String.class) { + if (numberOfColumns != 1) + throw new NiHaoException("Query too many columns for basic result type"); + while (rs.next()) { + l.add(returnAS(rs, 1, returnType)); + if ((limit != -1) && (--limit) == 0) + break; + } + } else if (HashMap.class.isAssignableFrom(returnType)) { + while (rs.next()) { + HashMap hm; + try { + hm = (HashMap) returnType.newInstance(); + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new NiHaoException("Can't create HashMap " + returnType.getName(), e); + } + for (int col = 1; col <= numberOfColumns; col++) + hm.put(rsmd.getColumnName(col), rs.getObject(col)); + l.add(hm); + if ((limit != -1) && (--limit) == 0) + break; + } + } else { + Method[] setters = new Method[numberOfColumns]; + for (int col = 1; col <= numberOfColumns; col++) { + Method m = Reflector.getSetter(returnType, rsmd.getColumnName(col)); + if (m == null) + throw new NiHaoException("Can't find setter for column '" + rsmd.getColumnName(col) + "' in " + returnType.getName()); + setters[col - 1] = m; + } + while (rs.next()) { + Object row; + try { + row = returnType.newInstance(); + } catch (InstantiationException e) { + throw new NiHaoException("Can't instantiate query result object type " + returnType.getName()); + } catch (IllegalAccessException e) { + throw new NiHaoException("Can't instantiate query result object type " + returnType.getName()); + } + for (int col = 0; col < numberOfColumns; col++) { + Object o = returnAS(rs, col + 1, setters[col].getParameterTypes()[0]); + try { + setters[col].invoke(row, o); + } catch (Throwable t) { + throw new NiHaoException("Cant set value for field " + setters[col].getName(), t); + } + } + l.add(row); + if ((limit != -1) && (--limit) == 0) + break; + } + } + s.close(); + return typeIt(l); + } + + /** + * Executes the query into database without retrieving result. + * + * @param con + * Connection + * @param param + * Object any object param + * @return int number of affected rows + * @throws SQLException + */ + public int execute(Connection con, Object param) throws SQLException { + PreparedStatement s = buildStatement(con, param); + int result = s.executeUpdate(); + s.close(); + return result; + } + + private Object typeIt(ArrayList l) { + int length = l.size(); + Object result = Array.newInstance(returnType, length); + for (int i = 0; i < length; i++) { + Object o = l.get(i); + Array.set(result, i, Reflector.compatibleCast(o, returnType)); + // Array.set(result, i, returnType.cast(o)); + // else + // throw new QCoreException("Can't cast from " + + // o.getClass().getName() + " to " + returnType.getName()); + } + return result; + } + + private static Object returnAS(ResultSet rs, int columnIndex, Class t) { + try { + if (t == String.class) + return rs.getString(columnIndex); + if (t == boolean.class || t == Boolean.class) + return rs.getBoolean(columnIndex); + if (t == byte.class || t == Byte.class) + return rs.getByte(columnIndex); + if (t == short.class || t == Short.class) + return rs.getShort(columnIndex); + if (t == int.class || t == Integer.class) + return rs.getInt(columnIndex); + if (t == long.class || t == Long.class) + return rs.getLong(columnIndex); + if (t == float.class || t == Float.class) + return rs.getFloat(columnIndex); + if (t == double.class || t == Double.class) + return rs.getDouble(columnIndex); + if (t == Date.class) + return rs.getDate(columnIndex); + if (t == Time.class) + return rs.getTime(columnIndex); + if (t == Timestamp.class) + return rs.getTimestamp(columnIndex); + if (t == URL.class) + return rs.getURL(columnIndex); + if (t == Url.class) + return new Url(rs.getString(columnIndex)); + if (t == byte[].class) { + Blob b = rs.getBlob(columnIndex); + return b.getBytes(0l, (int) b.length()); + } + if (t == InputStream.class) + return rs.getBlob(columnIndex).getBinaryStream(); + throw new NiHaoException("Unimplemented for type " + t.getName()); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + /** + * Retorna el identificador de la query + * + * @return + */ + public String getId() { + return id; + } + + /** + * Retorna el engine de la query + * + * @return + */ + public String getEngine() { + return engine; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(query); + sb.append('{'); + for (int i = 0; i < params.length; i++) { + if (i != 0) + sb.append(','); + sb.append(params[i].toString()); + } + sb.append('}'); + return sb.toString(); + } +} diff --git a/NiHao/src/nihao/db/QueryParam.java b/NiHao/src/nihao/db/QueryParam.java new file mode 100644 index 0000000..ad35989 --- /dev/null +++ b/NiHao/src/nihao/db/QueryParam.java @@ -0,0 +1,86 @@ +package nihao.db; + +import java.util.ArrayList; + +import nihao.context.ContextParseException; +import nihao.tokenizer.QToken; +import nihao.tokenizer.QTokenType; +import nihao.tokenizer.QTokenizer; + +public class QueryParam { + private IQueryParamElement[] element; + + public static QueryParam[] parse(String[] queryparams) { + QueryParam[] result = new QueryParam[queryparams.length]; + for (int i = 0; i < result.length; i++) + result[i] = parse(queryparams[i]); + return result; + } + + public static QueryParam parse(String queryparam) { + queryparam = queryparam.trim(); + QueryParam result = new QueryParam(); + if (queryparam.length() == 0) + return result; + ArrayList elements = new ArrayList(); + QTokenizer tkn = new QTokenizer(queryparam); + QToken t; + while ((t = tkn.next()) != null) { + if ("[".equals(t.getValue())) { + t = tkn.next(); + if (t == null) + throw new ContextParseException("Query parameter format error: '" + queryparam + "'"); + if (t.getType() != QTokenType.NUMBER) + throw new ContextParseException("Query parameter format error: '" + queryparam + "'"); + elements.add(new QueryParamElementArray(Integer.parseInt(t.getValue()))); + t = tkn.next(); + if (t == null) + throw new ContextParseException("Query parameter format error: '" + queryparam + "'"); + if (!"]".equals(t.getValue())) + throw new ContextParseException("Query parameter format error: '" + queryparam + "'"); + } else { + if (t.getType() != QTokenType.WORD) + throw new ContextParseException("Query parameter format error: '" + queryparam + "'"); + elements.add(new QueryParamElementNode(t.getValue())); + } + t = tkn.next(); + if (t != null) + if (!".".equals(t.getValue()) && !"[".equals(t.getValue())) + throw new ContextParseException("Query parameter format error: '" + queryparam + "'"); + } + result.element = elements.toArray(new IQueryParamElement[elements.size()]); + return result; + } + + /** + * Evalua el objeto para extraer los datos según la infomración de + * parametros + * + * @param o + * Object + * @return Object + */ + public QueryParamValue evaluate(Object o) { + if (element == null || element.length == 0) + return new QueryParamValue(null, o); + QueryParamValue val = null; + for (IQueryParamElement p : element) { + val = p.evaluate(o); + o = val.value; + } + return val; + } + + @Override + public String toString() { + if (element == null) + return "?"; + StringBuilder sb = new StringBuilder(); + for (IQueryParamElement e : element) { + if (sb.length() > 0) + sb.append('.'); + sb.append(e.toString()); + } + return sb.toString(); + } +} diff --git a/NiHao/src/nihao/db/QueryParamElementArray.java b/NiHao/src/nihao/db/QueryParamElementArray.java new file mode 100644 index 0000000..a91c7fc --- /dev/null +++ b/NiHao/src/nihao/db/QueryParamElementArray.java @@ -0,0 +1,27 @@ +package nihao.db; + +import java.lang.reflect.Array; +import java.util.List; + +public class QueryParamElementArray implements IQueryParamElement { + private int index; + + public QueryParamElementArray(int index) { + this.index = index; + } + + @Override + public QueryParamValue evaluate(Object o) { + if (o == null) + return null; + if (o instanceof List) + return new QueryParamValue(null, ((List) o).get(index)); + else + return new QueryParamValue(o.getClass().getComponentType(), Array.get(o, index)); + } + + @Override + public String toString() { + return "[" + index + "]"; + } +} diff --git a/NiHao/src/nihao/db/QueryParamElementNode.java b/NiHao/src/nihao/db/QueryParamElementNode.java new file mode 100644 index 0000000..b93af3c --- /dev/null +++ b/NiHao/src/nihao/db/QueryParamElementNode.java @@ -0,0 +1,46 @@ +package nihao.db; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; + +import nihao.NiHaoException; +import nihao.util.reflection.Reflector; + +public class QueryParamElementNode implements IQueryParamElement { + private String name; + + public QueryParamElementNode(String name) { + this.name = name; + } + + @Override + public QueryParamValue evaluate(Object o) { + if (o == null) + return null; + if (o instanceof Map) { + Map m = (Map) o; + if (m.containsKey(name)) + return new QueryParamValue(null, m.get(name)); + throw new NiHaoException("Query map don't contain element '" + name + "'"); + } else { + Class cls = o.getClass(); + String methodName = Reflector.getGetterName(name, cls == boolean.class); + Method m = Reflector.getCompatibleMethod(cls, methodName); + try { + return new QueryParamValue(m.getReturnType(), m.invoke(o)); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public String toString() { + return name; + } +} diff --git a/NiHao/src/nihao/db/QueryParamValue.java b/NiHao/src/nihao/db/QueryParamValue.java new file mode 100644 index 0000000..adef46b --- /dev/null +++ b/NiHao/src/nihao/db/QueryParamValue.java @@ -0,0 +1,39 @@ +package nihao.db; + +import java.sql.Types; +import java.util.Date; + +import nihao.NiHaoException; + +public class QueryParamValue { + Class type; + Object value; + int sqlType=-1; + + public QueryParamValue(Class c, Object o) { + if (c == null) + if (o != null) + c = o.getClass(); + type = c; + value = o; + if (c == null) + sqlType=Types.NULL; + else + if (c.isAssignableFrom(byte[].class)) + sqlType = Types.BLOB; + else if (c.isArray()) + sqlType = Types.ARRAY; + else if (c == long.class || c == Long.class) + sqlType = Types.BIGINT; + else if (c == boolean.class || c == Boolean.class) + sqlType = Types.BOOLEAN; + else if (c == Date.class) + sqlType = Types.DATE; + else if (c == double.class || c == Double.class) + sqlType = Types.DECIMAL; + else if (c == String.class) + sqlType = Types.VARCHAR; + else + throw new NiHaoException("Query unreconozable object type"); + } +} diff --git a/NiHao/src/nihao/dto/ChangesetDTO.java b/NiHao/src/nihao/dto/ChangesetDTO.java new file mode 100644 index 0000000..de8aeb7 --- /dev/null +++ b/NiHao/src/nihao/dto/ChangesetDTO.java @@ -0,0 +1,42 @@ +package nihao.dto; + +import java.sql.Timestamp; + +public class ChangesetDTO { + private String id; + private String author; + private Timestamp executed; + private String hash; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public Timestamp getExecuted() { + return executed; + } + + public void setExecuted(Timestamp executed) { + this.executed = executed; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } +} diff --git a/NiHao/src/nihao/log/ConsoleLogProvider.java b/NiHao/src/nihao/log/ConsoleLogProvider.java new file mode 100644 index 0000000..68566f1 --- /dev/null +++ b/NiHao/src/nihao/log/ConsoleLogProvider.java @@ -0,0 +1,19 @@ +package nihao.log; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class ConsoleLogProvider extends LogProvider { + static SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yy HH:mm:ss"); + + @Override + public void writeLog(long timestamp, LogLevel level, String s) { + System.out.print('['); + System.out.print(sdf.format(new Date(timestamp))); + System.out.print("] "); + System.out.print(level.name()); + System.out.print(": "); + System.out.println(s); + } + +} diff --git a/NiHao/src/nihao/log/LogConf.java b/NiHao/src/nihao/log/LogConf.java new file mode 100644 index 0000000..715fbce --- /dev/null +++ b/NiHao/src/nihao/log/LogConf.java @@ -0,0 +1,5 @@ +package nihao.log; + +public class LogConf { + +} diff --git a/NiHao/src/nihao/log/LogLevel.java b/NiHao/src/nihao/log/LogLevel.java new file mode 100644 index 0000000..bcc01b3 --- /dev/null +++ b/NiHao/src/nihao/log/LogLevel.java @@ -0,0 +1,5 @@ +package nihao.log; + +public enum LogLevel { + DEBUG, INFO, WARNING, ERROR +} diff --git a/NiHao/src/nihao/log/LogProvider.java b/NiHao/src/nihao/log/LogProvider.java new file mode 100644 index 0000000..50c5037 --- /dev/null +++ b/NiHao/src/nihao/log/LogProvider.java @@ -0,0 +1,58 @@ +package nihao.log; + +import nihao.NiHao; +import nihao.Provider; +import nihao.ProviderInfo; + +public abstract class LogProvider extends Provider { + private static ProviderInfo info = ProviderInfo.get(LogProvider.class,NilLogProvider.class); + private static LogProvider prov; + + public static LogProvider getProvider() { + if (prov == null) { + prov = instanceAs(info, LogProvider.class); + if (prov == null) + prov = new NilLogProvider(); + } + return prov; + } + + public static LogProvider getProvider(String name) { + return NiHao.getAs(name, LogProvider.class); + } + + public abstract void writeLog(long timestamp, LogLevel level, String s); + + public void debug(Object... o) { + writeLog(System.currentTimeMillis(), LogLevel.DEBUG, compute(o)); + } + + public void info(Object... o) { + writeLog(System.currentTimeMillis(), LogLevel.INFO, compute(o)); + } + + public void warn(Object... o) { + writeLog(System.currentTimeMillis(), LogLevel.WARNING, compute(o)); + } + + public void error(Object... o) { + writeLog(System.currentTimeMillis(), LogLevel.ERROR, compute(o)); + } + + public void exception(Throwable t) { + writeLog(System.currentTimeMillis(), LogLevel.ERROR, compute(new Object[] { t })); + } + + private String compute(Object[] data) { + StringBuffer sb = new StringBuffer(); + for (Object o : data) { + if (sb.length() > 0) + sb.append(' '); + if (o instanceof Throwable) { + + } else + sb.append(o); + } + return sb.toString(); + } +} diff --git a/NiHao/src/nihao/log/NilLogProvider.java b/NiHao/src/nihao/log/NilLogProvider.java new file mode 100644 index 0000000..6e98fcd --- /dev/null +++ b/NiHao/src/nihao/log/NilLogProvider.java @@ -0,0 +1,9 @@ +package nihao.log; + +public class NilLogProvider extends LogProvider { + + @Override + public void writeLog(long timestamp, LogLevel level, String s) { + } + +} diff --git a/NiHao/src/nihao/login/Group.java b/NiHao/src/nihao/login/Group.java new file mode 100644 index 0000000..9005535 --- /dev/null +++ b/NiHao/src/nihao/login/Group.java @@ -0,0 +1,26 @@ +package nihao.login; + +import java.io.Serializable; + +public class Group implements Serializable { + private static final long serialVersionUID = 1L; + + private int id; + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/NiHao/src/nihao/login/ILoginModule.java b/NiHao/src/nihao/login/ILoginModule.java new file mode 100644 index 0000000..d13e0d6 --- /dev/null +++ b/NiHao/src/nihao/login/ILoginModule.java @@ -0,0 +1,7 @@ +package nihao.login; + +import nihao.WebCall; + +public interface ILoginModule { + public User login(WebCall call); +} diff --git a/NiHao/src/nihao/login/LoginConf.java b/NiHao/src/nihao/login/LoginConf.java new file mode 100644 index 0000000..6003b17 --- /dev/null +++ b/NiHao/src/nihao/login/LoginConf.java @@ -0,0 +1,43 @@ +package nihao.login; + +public class LoginConf { + private ILoginModule[] modules; + private String pageLogin; + private String pageEnd; + private String pageError; + private String pageForbidden; + private String[] excluded; + private boolean disabled; + + public ILoginModule[] getModules() { + return modules; + } + + public String getPageLogin() { + return pageLogin; + } + + public String getPageEnd() { + return pageEnd; + } + + public String getPageError() { + return pageError; + } + + public String getPageForbidden() { + return pageForbidden; + } + + public String[] getExcluded() { + return excluded; + } + + /** + * True if all login and page security is down (not recommended) + * @return + */ + public boolean isDisabled() { + return disabled; + } +} diff --git a/NiHao/src/nihao/login/LoginDataManager.java b/NiHao/src/nihao/login/LoginDataManager.java new file mode 100644 index 0000000..c21155a --- /dev/null +++ b/NiHao/src/nihao/login/LoginDataManager.java @@ -0,0 +1,57 @@ +package nihao.login; + +import java.util.HashMap; + +import nihao.NiHaoException; +import nihao.db.DataManager; +import nihao.db.DataSourceProvider; +import nihao.login.beans.DBUser; +import nihao.util.Conversor; +import nihao.util.Hasher; + +public class LoginDataManager extends DataManager { + + public LoginDataManager() { + super(); + } + + public LoginDataManager(String connection) { + super(connection); + } + + public LoginDataManager(DataSourceProvider connection) { + super(connection); + } + + public User getUserByNickPwd(String nick, String pwd) { + HashMap map = new HashMap(); + map.put("nick", nick); + map.put("pwd", Conversor.bytesToHex(Hasher.SHA1(Conversor.utf8ToBytes(pwd)))); + DBUser[] users = selectAs("login_getUserByNickPwd", map, DBUser[].class); + if (users.length == 0) + return null; + if (users.length > 1) + throw new NiHaoException("Inconsistance error"); + return users[0].toUser(); + } + + public User getUserByNick(String nick) { + HashMap map = new HashMap(); + map.put("nick", nick); + DBUser[] users = selectAs("login_getUserByNick", map, DBUser[].class); + if (users.length == 0) + return null; + if (users.length > 1) + throw new NiHaoException("Inconsistance error"); + return users[0].toUser(); + } + + public Group[] getUserGroups(User user) { + Group[] result = selectAs("login_getGroupsForUser", user, Group[].class); + return result; + } + + public Group getGroupByName(String name) { + return selectAs("login_getGroupByName", name,Group.class); + } +} diff --git a/NiHao/src/nihao/login/LoginModuleRequest.java b/NiHao/src/nihao/login/LoginModuleRequest.java new file mode 100644 index 0000000..0545d8e --- /dev/null +++ b/NiHao/src/nihao/login/LoginModuleRequest.java @@ -0,0 +1,26 @@ +package nihao.login; + +import nihao.WebCall; + +public class LoginModuleRequest implements ILoginModule{ + private String userParamId = "user"; + private String passwordParamId = "pwd"; + + @Override + public User login(WebCall call) { + String usr = call.getParameter(userParamId); + String pwd = call.getParameter(passwordParamId); + if (usr==null || pwd==null) + return null; + LoginDataManager ldm=new LoginDataManager(); + return ldm.getUserByNickPwd(usr, pwd); + } + + public void setUserParamId(String userParamId) { + this.userParamId = userParamId; + } + + public void setPasswordParamId(String passwordParamId) { + this.passwordParamId = passwordParamId; + } +} diff --git a/NiHao/src/nihao/login/LoginModuleSSL.java b/NiHao/src/nihao/login/LoginModuleSSL.java new file mode 100644 index 0000000..de357f4 --- /dev/null +++ b/NiHao/src/nihao/login/LoginModuleSSL.java @@ -0,0 +1,29 @@ +package nihao.login; + +import java.security.cert.X509Certificate; + +import nihao.WebCall; + +public class LoginModuleSSL implements ILoginModule { + private String sslCertificateAttributeName = "javax.servlet.request.X509Certificate"; + + @Override + public User login(WebCall call) { + X509Certificate[] certs = call.getSSLCertificates(sslCertificateAttributeName); + if (certs==null) + return null; + if (certs.length==0) + return null; + X509Certificate user=certs[0]; + String sub=user.getSubjectDN().toString(); + //realizar un ocsp del certificado + //obtener DNI + if (!sub.contains("DNI")) + return null; + return null; + } + + public void setSslCertificateAttributeName(String sslCertificateAttributeName) { + this.sslCertificateAttributeName = sslCertificateAttributeName; + } +} diff --git a/NiHao/src/nihao/login/User.java b/NiHao/src/nihao/login/User.java new file mode 100644 index 0000000..927e1ab --- /dev/null +++ b/NiHao/src/nihao/login/User.java @@ -0,0 +1,58 @@ +package nihao.login; + +import java.io.Serializable; + +public class User implements Serializable { + private static final long serialVersionUID = 1L; + + private long id; + private String nick; + private String name; + private String surname; + private Group[] groups; + + public User(long id) { + this.id = id; + } + + public String getNick() { + return nick; + } + + public void setNick(String nick) { + this.nick = nick; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + + public Group[] getGroups() { + return groups; + } + + public void setGroups(Group[] groups) { + this.groups = groups; + } + + public long getId() { + return id; + } + + @Override + public String toString() { + return id + ": " + nick; + } +} diff --git a/NiHao/src/nihao/login/beans/DBUser.java b/NiHao/src/nihao/login/beans/DBUser.java new file mode 100644 index 0000000..15980cf --- /dev/null +++ b/NiHao/src/nihao/login/beans/DBUser.java @@ -0,0 +1,49 @@ +package nihao.login.beans; + +import nihao.login.User; + +public class DBUser { + private long id; + private String nick; + private String display; + private String pwd; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getNick() { + return nick; + } + + public void setNick(String nick) { + this.nick = nick; + } + + public String getDisplay() { + return display; + } + + public void setDisplay(String display) { + this.display = display; + } + + public String getPwd() { + return pwd; + } + + public void setPwd(String pwd) { + this.pwd = pwd; + } + + public User toUser(){ + User result=new User(id); + result.setNick(nick); + result.setName(display); + return result; + } +} diff --git a/NiHao/src/nihao/tags/Else.java b/NiHao/src/nihao/tags/Else.java new file mode 100644 index 0000000..67dd6f8 --- /dev/null +++ b/NiHao/src/nihao/tags/Else.java @@ -0,0 +1,14 @@ +package nihao.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.TagSupport; + +public class Else extends TagSupport { + private static final long serialVersionUID = 1L; + + @Override + public int doStartTag() throws JspException { + boolean enter = pageContext.getAttribute(If.ATTRIBUTE_NAME) != Boolean.TRUE; + return enter ? EVAL_BODY_INCLUDE : SKIP_BODY; + } +} diff --git a/NiHao/src/nihao/tags/ForEach.java b/NiHao/src/nihao/tags/ForEach.java new file mode 100644 index 0000000..ca81b1f --- /dev/null +++ b/NiHao/src/nihao/tags/ForEach.java @@ -0,0 +1,51 @@ +package nihao.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.BodyTagSupport; + +import nihao.NiHaoException; +import nihao.util.cursor.Cursor; + +public class ForEach extends BodyTagSupport { + private static final long serialVersionUID = 1L; + + private String as; + + @Override + public int doStartTag() throws JspException { + Cursor c = Cursor.getCursor(NiHaoTag.getWSValue(id, pageContext)); + pageContext.setAttribute("@C:" + as, c); + if (!c.hasMoreElements()) + return SKIP_BODY; + pageContext.setAttribute(as, c.next()); + return EVAL_BODY_INCLUDE; + } + + @Override + public int doAfterBody() throws JspException { + Cursor c = (Cursor) pageContext.getAttribute("@C:" + as); + if (!c.hasMoreElements()) + return SKIP_BODY; + pageContext.setAttribute(as, c.next()); + return EVAL_BODY_AGAIN; + } + + @Override + public int doEndTag() throws JspException { + pageContext.removeAttribute("@C:" + as); + pageContext.removeAttribute(as); + return EVAL_PAGE; + } + + public String getAs() { + return as; + } + + public void setAs(String as) { + if (as == null) + throw new NiHaoException("Q:ForEach.as can't be null"); + if (!as.startsWith("|")) + throw new NiHaoException("Q:ForEach.as must start with \"|\""); + this.as = as; + } +} diff --git a/NiHao/src/nihao/tags/If.java b/NiHao/src/nihao/tags/If.java new file mode 100644 index 0000000..85e3cdc --- /dev/null +++ b/NiHao/src/nihao/tags/If.java @@ -0,0 +1,40 @@ +package nihao.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.TagSupport; + +import nihao.NiHaoException; + +public class If extends TagSupport { + private static final long serialVersionUID = 1L; + + static final String ATTRIBUTE_NAME = "@IF@"; + + private String eval; + + @Override + public int doStartTag() throws JspException { + boolean enter; + if (id != null) { + Object o = NiHaoTag.getWSValue(id, pageContext); + if (o instanceof Boolean) + enter = o == Boolean.TRUE; + else + enter = o != null; + } else { + if (eval == null) + throw new NiHaoException("Q:If needs id or eval attributes"); + throw new NiHaoException("Q:If eval not implemented"); + } + pageContext.setAttribute(ATTRIBUTE_NAME, enter); + return enter ? EVAL_BODY_INCLUDE : SKIP_BODY; + } + + public String getEval() { + return eval; + } + + public void setEval(String eval) { + this.eval = eval; + } +} diff --git a/NiHao/src/nihao/tags/Loop.java b/NiHao/src/nihao/tags/Loop.java new file mode 100644 index 0000000..74aa201 --- /dev/null +++ b/NiHao/src/nihao/tags/Loop.java @@ -0,0 +1,60 @@ +package nihao.tags; + +import java.io.IOException; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.BodyTagSupport; + +import nihao.NiHaoException; + +public class Loop extends BodyTagSupport { + private static final long serialVersionUID = 1L; + + int times = 0; + + @Override + public int doStartTag() throws JspException { + if (id != null) { + Object o = NiHaoTag.getWSValue(id, pageContext); + if (o instanceof Integer) + this.times = (Integer) o; + else if (o instanceof String) + this.times = Integer.parseInt((String) o); + else + throw new NiHaoException("Q:Loop times value for '" + times + "' is not integer"); + } + if (times > 0) { + return EVAL_BODY_BUFFERED; + } else { + return SKIP_BODY; + } + } + + public int doAfterBody() throws JspException { + if (times > 1) { + times--; + return EVAL_BODY_AGAIN; + } else { + return SKIP_BODY; + } + } + + public int doEndTag() throws JspException { + try { + if (bodyContent != null) { + bodyContent.writeOut(bodyContent.getEnclosingWriter()); + } + } catch (IOException e) { + throw new JspException("Error: " + e.getMessage()); + } + return EVAL_PAGE; + } + + public int getTimes() { + return times; + } + + public void setTimes(int times) { + this.times = times; + } +} diff --git a/NiHao/src/nihao/tags/NiHaoTag.java b/NiHao/src/nihao/tags/NiHaoTag.java new file mode 100644 index 0000000..6b9a08b --- /dev/null +++ b/NiHao/src/nihao/tags/NiHaoTag.java @@ -0,0 +1,181 @@ +package nihao.tags; + +import java.io.IOException; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.PageContext; +import javax.servlet.jsp.tagext.TagSupport; + +import nihao.NiHaoException; +import nihao.WebCall; +import nihao.tags.types.TagValueType; +import nihao.tags.util.TagBuilder; +import nihao.util.reflection.Reflector; + +public abstract class NiHaoTag extends TagSupport { + private static final long serialVersionUID = 1L; + protected TagValueType valueType = TagValueType.AUTO; + + protected String htmlid = null; + protected String styleclass = null; + protected String fname; + protected String value; + + protected void initValues() { + fname = "::" + id; + if (htmlid == null) + htmlid = fname; + } + + private static Object getContextValue(String name, PageContext pageContext) { + if (!name.contains(".")) + return pageContext.getAttribute(name); + String[] names = name.split("\\."); + Object o = pageContext.getAttribute(names[0]); + if (o == null) + return null; + return Reflector.getPathValue(o, names, 1, Object.class); + } + + protected Object getWSValue(String name) { + if (name == null) + return null; + if (name.startsWith("|")) + return getContextValue(name, pageContext); + WebCall c = WebCall.getWebCall(pageContext.getRequest()); + return c.getValue(name); + } + + protected String getWSValueString(String name) { + Object v = getWSValue(name); + if (v == null) + return null; + return v.toString(); + } + + static Object getWSValue(String name, PageContext pageContext) { + if (name.startsWith("|")) + return getContextValue(name, pageContext); + WebCall c = WebCall.getWebCall(pageContext.getRequest()); + return c.getValue(name); + } + + static String getWSValueString(String name, PageContext pageContext) { + Object v = getWSValue(name, pageContext); + if (v == null) + return null; + return v.toString(); + } + + protected WebCall getWebCall() { + return WebCall.getWebCall(pageContext.getRequest()); + } + + protected String getDisplayValue() { + String result; + switch (valueType) { + case AUTO: + if (value == null) + result = getWSValueString(id); + else + result = value; + break; + case NONE: + result = ""; + break; + case WORKSET: + result = getWSValueString(id); + break; + case VALUE: + result = value; + break; + default: + throw new NiHaoException("Unknown ValueType on Tag"); + } + return result; + } + + protected Object getValue() { + Object result; + switch (valueType) { + case AUTO: + if (value == null) + result = getWSValue(id); + else + result = value; + break; + case NONE: + result = ""; + break; + case WORKSET: + result = getWSValue(id); + break; + case VALUE: + result = value; + break; + default: + throw new NiHaoException("Unknown ValueType on Tag"); + } + return result; + } + + protected void fillTag(TagBuilder tb) { + tb.setAttr("name", fname); + tb.setAttr("id", htmlid); + if (styleclass != null) + tb.setAttr("class", styleclass); + setValue(tb); + } + + protected abstract void setValue(TagBuilder tb); + + protected void write(String s) { + try { + pageContext.getOut().write(s); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + protected void write(TagBuilder tb) { + write(tb.toString()); + } + + public String getHtmlid() { + return htmlid; + } + + public void setHtmlid(String htmlid) { + this.htmlid = htmlid; + } + + public String getStyleclass() { + return styleclass; + } + + public void setStyleclass(String styleclass) { + this.styleclass = styleclass; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public int doAfterBody() throws JspException { + System.out.println("doAfterBody()"); + return super.doAfterBody(); + } + + @Override + public int doEndTag() throws JspException { + System.out.println("doEndTag()"); + return super.doEndTag(); + } + + @Override + public int doStartTag() throws JspException { + System.out.println("doStartTag()"); + return super.doStartTag(); + } +} diff --git a/NiHao/src/nihao/tags/NiHaoTagInput.java b/NiHao/src/nihao/tags/NiHaoTagInput.java new file mode 100644 index 0000000..688a59c --- /dev/null +++ b/NiHao/src/nihao/tags/NiHaoTagInput.java @@ -0,0 +1,12 @@ +package nihao.tags; + +import nihao.tags.util.TagBuilder; + +public class NiHaoTagInput extends NiHaoTag { + private static final long serialVersionUID = 1L; + + @Override + protected void setValue(TagBuilder tb) { + tb.setAttr("value", getDisplayValue()); + } +} diff --git a/NiHao/src/nihao/tags/PasswordBox.java b/NiHao/src/nihao/tags/PasswordBox.java new file mode 100644 index 0000000..1090769 --- /dev/null +++ b/NiHao/src/nihao/tags/PasswordBox.java @@ -0,0 +1,27 @@ +package nihao.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.Tag; + +import nihao.tags.util.TagBuilder; + +public class PasswordBox extends NiHaoTagInput{ + private static final long serialVersionUID = 1L; + + + + @Override + public int doStartTag() throws JspException { + return Tag.SKIP_BODY; + } + + @Override + public int doEndTag() throws JspException { + initValues(); + TagBuilder tb=new TagBuilder("input"); + tb.setAttr("type", "password"); + fillTag(tb); + write(tb); + return Tag.EVAL_PAGE; + } +} diff --git a/NiHao/src/nihao/tags/Submit.java b/NiHao/src/nihao/tags/Submit.java new file mode 100644 index 0000000..d351c0c --- /dev/null +++ b/NiHao/src/nihao/tags/Submit.java @@ -0,0 +1,51 @@ +package nihao.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.Tag; + +import nihao.tags.util.TagBuilder; + +public class Submit extends NiHaoTag { + private static final long serialVersionUID = 1L; + + private String label; + private String execute; + + @Override + public int doStartTag() throws JspException { + return Tag.SKIP_BODY; + } + + @Override + public int doEndTag() throws JspException { + TagBuilder tb = new TagBuilder("input"); + tb.setAttr("type", "submit"); + if (styleclass != null) + tb.setAttr("class", styleclass); + tb.setAttr("value", label); + if (execute != null) + tb.setAttr("name", "@" + execute); + write(tb); + return Tag.EVAL_PAGE; + } + + @Override + protected void setValue(TagBuilder tb) { + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getExecute() { + return execute; + } + + public void setExecute(String execute) { + this.execute = execute; + } +} diff --git a/NiHao/src/nihao/tags/Text.java b/NiHao/src/nihao/tags/Text.java new file mode 100644 index 0000000..79a4ac3 --- /dev/null +++ b/NiHao/src/nihao/tags/Text.java @@ -0,0 +1,25 @@ +package nihao.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.Tag; + +import nihao.tags.util.TagBuilder; + +public class Text extends NiHaoTag{ + private static final long serialVersionUID = 1L; + + @Override + public int doStartTag() throws JspException { + return Tag.SKIP_BODY; + } + + @Override + public int doEndTag() throws JspException { + write(getWSValueString(id)); + return Tag.EVAL_PAGE; + } + + @Override + protected void setValue(TagBuilder tb) { + } +} diff --git a/NiHao/src/nihao/tags/TextBox.java b/NiHao/src/nihao/tags/TextBox.java new file mode 100644 index 0000000..9b07855 --- /dev/null +++ b/NiHao/src/nihao/tags/TextBox.java @@ -0,0 +1,26 @@ +package nihao.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.Tag; + +import nihao.tags.util.TagBuilder; + +public class TextBox extends NiHaoTagInput{ + private static final long serialVersionUID = 1L; + + + @Override + public int doStartTag() throws JspException { + return Tag.SKIP_BODY; + } + + @Override + public int doEndTag() throws JspException { + initValues(); + TagBuilder tb=new TagBuilder("input"); + tb.setAttr("type", "text"); + fillTag(tb); + write(tb); + return Tag.EVAL_PAGE; + } +} diff --git a/NiHao/src/nihao/tags/Violation.java b/NiHao/src/nihao/tags/Violation.java new file mode 100644 index 0000000..38e0b34 --- /dev/null +++ b/NiHao/src/nihao/tags/Violation.java @@ -0,0 +1,19 @@ +package nihao.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.TagSupport; + +import nihao.WebCall; + +public class Violation extends TagSupport { + private static final long serialVersionUID = 1L; + + @Override + public int doStartTag() throws JspException { + WebCall call = WebCall.getWebCall(pageContext.getRequest()); + String[] vios = call.getValidationViolations(id); + if (vios == null) + return SKIP_BODY; + return EVAL_BODY_INCLUDE; + } +} diff --git a/NiHao/src/nihao/tags/types/TagValueType.java b/NiHao/src/nihao/tags/types/TagValueType.java new file mode 100644 index 0000000..f1816a1 --- /dev/null +++ b/NiHao/src/nihao/tags/types/TagValueType.java @@ -0,0 +1,5 @@ +package nihao.tags.types; + +public enum TagValueType { + AUTO,VALUE,WORKSET,NONE; +} diff --git a/NiHao/src/nihao/tags/util/TagBuilder.java b/NiHao/src/nihao/tags/util/TagBuilder.java new file mode 100644 index 0000000..3d2a3b0 --- /dev/null +++ b/NiHao/src/nihao/tags/util/TagBuilder.java @@ -0,0 +1,54 @@ +package nihao.tags.util; + +import nihao.NiHaoException; + +public class TagBuilder { + private StringBuilder sb = new StringBuilder(1024); + + private String tagName; + private boolean body = false; + private boolean close = false; + + public TagBuilder(String tagName) { + this.tagName = tagName; + sb.append('<'); + sb.append(tagName); + } + + public void setAttr(String name, String value) { + if (body) + throw new NiHaoException("Can't build tag attribute before tag body"); + sb.append(' '); + sb.append(name); + sb.append('='); + sb.append('"'); + sb.append(value); + sb.append('"'); + } + + public void setBody(String body) { + if (this.body) + throw new NiHaoException("Can't build multiple tag bodyes"); + this.body = true; + sb.append('>'); + sb.append(body); + sb.append('<'); + } + + public void close() { + if (body) { + sb.append('/'); + sb.append(tagName); + sb.append('>'); + } else + sb.append("/>"); + close = true; + } + + @Override + public String toString() { + if (!close) + close(); + return sb.toString(); + } +} diff --git a/NiHao/src/nihao/tokenizer/QToken.java b/NiHao/src/nihao/tokenizer/QToken.java new file mode 100644 index 0000000..15f46b6 --- /dev/null +++ b/NiHao/src/nihao/tokenizer/QToken.java @@ -0,0 +1,36 @@ +package nihao.tokenizer; + +public class QToken { + private QTokenType type; + private String value; + + public QToken(QTokenType type, String value) { + this.type = type; + this.value = value; + } + + public QTokenType getType() { + return type; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return type.name() + " " + value; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof QToken)) + return false; + QToken t = (QToken) o; + return t.type == type && value.equals(t.value); + } + + public boolean equals(QTokenType type, String value) { + return this.type == type && this.value.equals(value); + } +} diff --git a/NiHao/src/nihao/tokenizer/QTokenType.java b/NiHao/src/nihao/tokenizer/QTokenType.java new file mode 100644 index 0000000..11bd8ac --- /dev/null +++ b/NiHao/src/nihao/tokenizer/QTokenType.java @@ -0,0 +1,5 @@ +package nihao.tokenizer; + +public enum QTokenType { + UNKNOWN, WORD, NUMBER, LITERAL, SYMBOL +} diff --git a/NiHao/src/nihao/tokenizer/QTokenizer.java b/NiHao/src/nihao/tokenizer/QTokenizer.java new file mode 100644 index 0000000..c70f44d --- /dev/null +++ b/NiHao/src/nihao/tokenizer/QTokenizer.java @@ -0,0 +1,70 @@ +package nihao.tokenizer; + +public class QTokenizer { + private static final String ignore = " \t\n\r"; + private static final String number = "0123456789."; + private static final String letters = "abcdefghijklmnñopqrstuvwxyzáéíóúàèìòùâêîôûäëïöüãõ_ABCDEFGHIJKLMNÑOPQRSTUVWXYZÁÉÍÓÚÀÈÌÒÙÂÊÎÔÛÄËÏÖÜÃÕ0123456789"; + private String s; + private int index = 0; + + public QTokenizer(String input) { + s = input; + } + + public QToken patrol() { + int i = index; + QToken result = next(); + index = i; + return result; + } + + public QToken next() { + if (index >= s.length()) + return null; + while (ignore.contains(new String(new char[] { s.charAt(index) }))) { + index++; + if (index >= s.length()) + return null; + } + char ch = s.charAt(index); + String range; + String sch = new String(new char[] { ch }); + if (number.contains(sch) && ch != '.') + range = number; + else if (letters.contains(sch)) + range = letters; + else { + index++; + if (ch == '"' || ch == '\'') { // String + StringBuilder sb = new StringBuilder(); + char ich; + while ((ich = s.charAt(index)) != ch) { + sb.append(ich); + index++; + if (index >= s.length()) + break; + } + index++; + return new QToken(QTokenType.LITERAL, sb.toString()); + } else if (index < s.length() && ch == '/' && s.charAt(index) == '/') { // Commentary jump + while (s.charAt(index) != '\n') { + index++; + if (index >= s.length()) + break; + } + return next(); + } else + return new QToken(QTokenType.SYMBOL, sch); + } + StringBuilder sb = new StringBuilder(); + while (range.contains(sch)) { + sb.append(ch); + index++; + if (index >= s.length()) + break; + ch = s.charAt(index); + sch = new String(new char[] { ch }); + } + return new QToken(range == number ? QTokenType.NUMBER : QTokenType.WORD, sb.toString()); + } +} diff --git a/NiHao/src/nihao/types/PageSecuityType.java b/NiHao/src/nihao/types/PageSecuityType.java new file mode 100644 index 0000000..18a988d --- /dev/null +++ b/NiHao/src/nihao/types/PageSecuityType.java @@ -0,0 +1,5 @@ +package nihao.types; + +public enum PageSecuityType { + PUBLIC, PRIVATE, BYGROUP +} diff --git a/NiHao/src/nihao/util/Base64Codec.java b/NiHao/src/nihao/util/Base64Codec.java new file mode 100644 index 0000000..b20166e --- /dev/null +++ b/NiHao/src/nihao/util/Base64Codec.java @@ -0,0 +1,183 @@ +package nihao.util; + +/** + * A Base64 encoder/decoder. + * + *

+ * This class is used to encode and decode data in Base64 format as described in + * RFC 1521. + * + *

+ * Project home page: www. + * source-code.biz/base64coder/java
+ * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
+ * Multi-licensed: EPL / LGPL / GPL / AL / BSD / MIT. + */ +public class Base64Codec { + // Mapping table from 6-bit nibbles to Base64 characters. + private static final byte[] map1 = new byte[64]; + static { + int i = 0; + for (char c = 'A'; c <= 'Z'; c++) + map1[i++] = (byte) c; + for (char c = 'a'; c <= 'z'; c++) + map1[i++] = (byte) c; + for (char c = '0'; c <= '9'; c++) + map1[i++] = (byte) c; + map1[i++] = '+'; + map1[i++] = '/'; + } + + // Mapping table from Base64 characters to 6-bit nibbles. + private static final byte[] map2 = new byte[128]; + static { + for (int i = 0; i < map2.length; i++) + map2[i] = -1; + for (int i = 0; i < 64; i++) + map2[map1[i]] = (byte) i; + } + + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted in the output. + * + * @param in + * An array containing the data bytes to be encoded. + * @return A character array containing the Base64 encoded data. + */ + public static byte[] encode(byte[] in) { + return encode(in, 0, in.length); + } + + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted in the output. + * + * @param in + * An array containing the data bytes to be encoded. + * @param iOff + * Offset of the first byte in in to be processed. + * @param iLen + * Number of bytes to process in in, starting at + * iOff. + * @return A character array containing the Base64 encoded data. + */ + public static byte[] encode(byte[] in, int iOff, int iLen) { + int oDataLen = (iLen * 4 + 2) / 3; // output length without padding + int oLen = ((iLen + 2) / 3) * 4; // output length including padding + byte[] out = new byte[oLen]; + int ip = iOff; + int iEnd = iOff + iLen; + int op = 0; + while (ip < iEnd) { + int i0 = in[ip++] & 0xff; + int i1 = ip < iEnd ? in[ip++] & 0xff : 0; + int i2 = ip < iEnd ? in[ip++] & 0xff : 0; + int o0 = i0 >>> 2; + int o1 = ((i0 & 3) << 4) | (i1 >>> 4); + int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); + int o3 = i2 & 0x3F; + out[op++] = map1[o0]; + out[op++] = map1[o1]; + out[op] = (byte) (op < oDataLen ? map1[o2] : '='); + op++; + out[op] = (byte) (op < oDataLen ? map1[o3] : '='); + op++; + } + return out; + } + + /** + * Decodes a byte array from Base64 format. No blanks or line breaks are + * allowed within the Base64 encoded input data. + * + * @param in + * A character array containing the Base64 encoded data. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static byte[] decode(byte[] in) { + return decode(in, 0, in.length); + } + + /** + * Decodes a byte array from Base64 format. + * + * @param in + * A character array containing the Base64 encoded data. + * @param iOff + * Offset of the first character in in to be + * processed. + * @param iLen + * Number of characters to process in in, starting + * at iOff. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static byte[] decode(byte[] in, int iOff, int iLen) { + int ip=0; + for (int i=0;iin to be + * processed. + * @param iLen + * Number of characters to process in in, starting + * at iOff. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + private static byte[] idecode(byte[] in, int iOff, int iLen) { + if (iLen % 4 != 0) + throw new IllegalArgumentException("Length of Base64 encoded input string is not a multiple of 4."); + while (iLen > 0 && in[iOff + iLen - 1] == '=') + iLen--; + int oLen = (iLen * 3) / 4; + byte[] out = new byte[oLen]; + int ip = iOff; + int iEnd = iOff + iLen; + int op = 0; + while (ip < iEnd) { + int i0 = in[ip++]; + int i1 = in[ip++]; + int i2 = ip < iEnd ? in[ip++] : 'A'; + int i3 = ip < iEnd ? in[ip++] : 'A'; + if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) + throw new IllegalArgumentException("Illegal character in Base64 encoded data."); + int b0 = map2[i0]; + int b1 = map2[i1]; + int b2 = map2[i2]; + int b3 = map2[i3]; + if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) + throw new IllegalArgumentException("Illegal character in Base64 encoded data."); + int o0 = (b0 << 2) | (b1 >>> 4); + int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); + int o2 = ((b2 & 3) << 6) | b3; + out[op++] = (byte) o0; + if (op < oLen) + out[op++] = (byte) o1; + if (op < oLen) + out[op++] = (byte) o2; + } + return out; + } + + private Base64Codec() { + } +} \ No newline at end of file diff --git a/NiHao/src/nihao/util/Conversor.java b/NiHao/src/nihao/util/Conversor.java new file mode 100644 index 0000000..636cd66 --- /dev/null +++ b/NiHao/src/nihao/util/Conversor.java @@ -0,0 +1,568 @@ +package nihao.util; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Reader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; + +import nihao.NiHaoException; + +public class Conversor { + static final String ASCIICharset = "ISO-8859-1"; + static final String UTF8Charset = "UTF-8"; + static final char[] HEX_CHAR_TABLE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + /** + * Genera un String con el hexadecimal de los datos + * + * @param data + * Array de bytes + * @return string en hexadecimal + */ + public static String bytesToHex(byte[] data) { + char[] result = new char[data.length * 2]; + int j = 0; + for (int i = 0; i < data.length; i++) { + byte b = data[i]; + result[j++] = HEX_CHAR_TABLE[(b & 0xF0) >> 4]; + result[j++] = HEX_CHAR_TABLE[b & 0x0F]; + } + return new String(result); + } + + /** + * Pasa un string en hexadecimal a datos binarios + * + * @param hex + * String en hexadecimal + * @return Array de bytes con los datos + */ + public static byte[] hexToBytes(String hex) { + byte[] result = new byte[hex.length() / 2]; + char[] hexc = hex.toCharArray(); + int j = 0; + for (int i = 0; i < result.length; i++) { + int b = charHexNoob(hexc[j++]) << 4; + b = b | charHexNoob(hexc[j++]); + result[i] = (byte) b; + } + return result; + } + + private static int charHexNoob(char c) { + for (int i = 0; i < HEX_CHAR_TABLE.length; i++) + if (HEX_CHAR_TABLE[i] == c) + return i; + return -1; + } + + /** + * Genera un string ISO-8859-1 a partir de bytes + * + * @param data + * Array de bytes + * @return String + */ + public static String bytesToASCII(byte[] data) { + try { + return new String(data, ASCIICharset); + } catch (UnsupportedEncodingException e) { + return new String(data); + } + } + + /** + * Genera un Array de bytes de un string en ISO-8859-1 + * + * @param ascii + * String + * @return Array de bytes + */ + public static byte[] asciiToBytes(String ascii) { + try { + return ascii.getBytes(ASCIICharset); + } catch (UnsupportedEncodingException e) { + return ascii.getBytes(); + } + } + + /** + * Genera un string a partir de unos bytes en UTF-8 + * + * @param data + * Array de bytes + * @return String + */ + public static String bytesToUTF8(byte[] data) { + try { + return new String(data, UTF8Charset); + } catch (UnsupportedEncodingException e) { + return new String(data); + } + } + + /** + * Genera un array de bytes a partir de una string en UTF-8 + * + * @param ascii + * String + * @return Array de bytes + */ + public static byte[] utf8ToBytes(String ascii) { + try { + return ascii.getBytes(UTF8Charset); + } catch (UnsupportedEncodingException e) { + return ascii.getBytes(); + } + } + + /** + * Genera una string con los daots en base64 + * + * @param data + * Array de bytes + * @return String + */ + public static String bytesToBase64(byte[] data) { + byte[] result = Base64Codec.encode(data); + return bytesToASCII(result); + } + + /** + * genera un Array de bytes a partir de su Base64 + * + * @param data64 + * String con el Base64 + * @return Array de bytes + */ + public static byte[] base64ToBytes(String data64) { + return Base64Codec.decode(asciiToBytes(data64)); + } + + /** + * genera un Array de bytes a partir de su Base64 + * + * @param data64 + * String con el Base64 + * @return Array de bytes + */ + public static byte[] base64ToBytes(byte[] data64) { + return Base64Codec.decode(data64); + } + + /** + * Array de bytes a binario + * + * @param data + * @return + */ + public static String bytesToBin(byte[] data) { + char[] result = new char[data.length * 9]; + int j = 0; + for (int i = 0; i < data.length; i++) { + byte b = data[i]; + result[j++] = ((b & 0x80) > 0) ? '1' : '0'; + result[j++] = ((b & 0x40) > 0) ? '1' : '0'; + result[j++] = ((b & 0x20) > 0) ? '1' : '0'; + result[j++] = ((b & 0x10) > 0) ? '1' : '0'; + + result[j++] = ((b & 0x08) > 0) ? '1' : '0'; + result[j++] = ((b & 0x04) > 0) ? '1' : '0'; + result[j++] = ((b & 0x02) > 0) ? '1' : '0'; + result[j++] = ((b & 0x01) > 0) ? '1' : '0'; + result[j++] = '-'; + } + return new String(result); + } + + /** + * Pasa un array de bytes a un string intentando detectar su encoding, + * admite BOM + * + * @param data + * @return + */ + public static String bytesToString(byte[] data) { + TextEncoding enc = getStringEncoding(data); + switch (enc) { + case ASCII: + return bytesToASCII(data); + case UTF8: + return bytesToUTF8(data); + case UTF16LE: + return silentEncode(data, "UTF-16LE"); + case UTF16BE: + return silentEncode(data, "UTF-16BE"); + default: + throw new RuntimeException("32Bits Encoding not suported"); + } + } + + /** + * Pasa un array de bytes a un string intentando detectar su encoding, + * admite BOM + * + * @param data + * byte[] + * @param maxlen + * int maximo de la cadena a retornar + * @return String + */ + public static String bytesToString(byte[] data, int maxlen) { + TextEncoding enc = getStringEncoding(data); + switch (enc) { + case ASCII: + return silentEncode(data, "ISO-8859-1", maxlen); + case UTF8: + return silentEncode(data, "UTF-8", maxlen); + case UTF16LE: + return silentEncode(data, "UTF-16LE", maxlen); + case UTF16BE: + return silentEncode(data, "UTF-16BE", maxlen); + default: + throw new RuntimeException("32Bits Encoding not suported"); + } + } + + /** + * Controla la excepcion del econding + * + * @param data + * byte[] de datos + * @param encode + * String con la descripcion del encoding + * @return + */ + private static String silentEncode(byte[] data, String encode) { + try { + return new String(data, encode); + } catch (Exception e) { + return bytesToASCII(data); + } + } + + /** + * Controla la excepcion del econding + * + * @param data + * byte[] de datos + * @param encode + * String con la descripcion del encoding + * @param maxlen + * int maximo de la cadena a retornar + * @return + */ + private static String silentEncode(byte[] data, String encode, int maxlen) { + try { + if (maxlen > data.length) + maxlen = data.length; + return new String(data, 0, maxlen, encode); + } catch (Exception e) { + return bytesToASCII(data); + } + } + + /** + * Detectamos el encoding de un string con su BOM o su inicio + * + * @param data + * @return encoding + */ + public static TextEncoding getStringEncoding(byte[] data) { + int lng = data.length; + if (lng == 0) + return TextEncoding.ASCII; + // BOM + if (lng >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF) + return TextEncoding.UTF8; + if (lng >= 2 && data[0] == 0xFF && data[1] == 0xFE) + return TextEncoding.UTF16LE; + if (lng >= 2 && data[0] == 0xFE && data[1] == 0xFF) + return TextEncoding.UTF16BE; + if (lng >= 4 && data[0] == 0xFF && data[1] == 0xFE && data[2] == 0x00 && data[3] == 0x00) + return TextEncoding.UTF32LE; + if (lng >= 4 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0xFE && data[3] == 0xFF) + return TextEncoding.UTF32BE; + // Inicio del texto (autodeteccion) + if (lng >= 4 && data[0] != 0x00 && data[1] == 0x00 && data[2] != 0x00 && data[3] == 0x00) + return TextEncoding.UTF16LE; + if (lng >= 4 && data[0] == 0x00 && data[1] != 0x00 && data[2] == 0x00 && data[3] != 0x00) + return TextEncoding.UTF16BE; + if (lng >= 2 && data[0] != 0x00 && data[1] == 0x00) + return TextEncoding.UTF16LE; + if (lng >= 2 && data[0] == 0x00 && data[1] != 0x00) + return TextEncoding.UTF16BE; + // Se asume ASCII + return TextEncoding.ASCII; + } + + /** + * Pasa una coleccion a un array del tipo indicato + */ + @SuppressWarnings("unchecked") + public static T[] toArray(Collection c, Class rtype) { + T[] result = (T[]) Array.newInstance(rtype, 0); + return c.toArray(result); + } + + /** + * Pasa un array a un ArrayList que lo contenga + */ + public static ArrayList toArrayList(T[] array) { + ArrayList result = new ArrayList(array.length); + for (T t : array) + result.add(t); + return result; + } + + /** + * Serializa un objeto en Base64 + * + * @param myObject + * Object objeto a serializar + * @return String con el Base64 de la serializaci�n del objeto + */ + public static String objectToBase64(Object myObject) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(myObject); + oos.flush(); + oos.close(); + bos.close(); + byte[] data = bos.toByteArray(); + return bytesToBase64(data); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Deserializa un Base64 a objeto + * + * @param myObject + * base64 del objeto serializado + * @return Objeto en cuenstion + */ + public static Object base64ToObject(String myObject) { + try { + byte[] data = base64ToBytes(myObject); + ObjectInputStream objectIn = null; + if (data == null) + return null; + objectIn = new ObjectInputStream(new ByteArrayInputStream(data)); + return objectIn.readObject(); + } catch (RuntimeException e) { + throw e; + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + /** + * Deserializa un Base64 a objeto + * + * @param + * Tipo de objeto de vuelta + * @param b64data + * base64 del objeto serializado + * @param resultType + * Clase de objeto + * @return Objeto en cuensti�n + */ + public static Object base64ToObject(String b64data, Class resultType) { + byte[] data = base64ToBytes(b64data); + return Serializer.deserialize(data, resultType); + } + + /** + * Intenta un cast, si no se consigue retorna null + * + * @param + * Tipo de vuelta + * @param o + * Objeto a realizar cast + * @param c + * Clase destino + * @return (T) o; + */ + public static T as(Object o, Class c) { + if (o == null) + return null; + if (c == String.class) + return c.cast(o.toString()); + if (c.isInstance(o)) + return c.cast(o); + else + return null; + } + + /** + * Intenta un cast, si no se consigue retorna null, metodo de rerror + * + * @param + * Tipo de vuelta + * @param o + * Objeto a realizar cast + * @return (T) o; + */ + @SuppressWarnings("unchecked") + public static T as(Object o) { + try { + return (T) o; + } catch (ClassCastException e) { + return null; + } + } + + /** + * toString de cualquier object (incluso nulos) + * + * @param o + * Object + * @return String + */ + public static String toString(Object o) { + if (o == null) + return null; + String result = o instanceof String ? (String) o : null; + if (result == null) + result = o.toString(); + return result; + } + + /** + * Quita el signo de un array de bytes + * + * @param data + * @param start + * @param length + * @return + */ + public static short[] unsignedBytes(byte[] data, int start, int length) { + short[] result = new short[length]; + for (int i = 0; i < length; i++) { + int j = i + start; + result[i] = (short) (0x000000FF & data[j]); + } + return result; + } + + /** + * Retorna true si yesornot es afirmativo + * + * @param yesornot + * String + * @return boolean + */ + public static boolean isYes(String yesornot) { + if (yesornot == null) + return false; + String[] yeses = { "y", "yes", "j", "ja", "s", "si", "1", "o", "oui", "x", "true" }; + for (String yes : yeses) + if (yes.equalsIgnoreCase(yesornot)) + return true; + return false; + } + + /** + * Lee el stream y genera un String con la entrada en UTF-8 + * + * @param is + * InputStream + * @return String + */ + public static String readToString(InputStream is) { + try { + if (is != null) { + Writer writer = new StringWriter(); + + char[] buffer = new char[1024]; + try { + Reader reader = new BufferedReader(new InputStreamReader(is, UTF8Charset)); + int n; + while ((n = reader.read(buffer)) != -1) { + writer.write(buffer, 0, n); + } + } finally { + is.close(); + } + return writer.toString(); + } else { + return ""; + } + } catch (Throwable t) { + if (t instanceof RuntimeException) + throw (RuntimeException) t; + throw new RuntimeException(t); + } + } + + /** + * Realiza el tostring con cotnrol de nulos + * + * @param o + * Object + * @param nval + * String del valor nulo + * @return String + */ + public static String nvl(Object o, String nval) { + return o==null?nval:o.toString(); + } + + /** + * Returns the string to the better number representative object(Double, + * Integer or Long) based on string value. + * + * For more controlled option use the StringToNumber with class parameter + * + * @param snum + * @return + */ + public static Object StringToNumber(String snum) { + return StringToNumber(snum, null); + } + + @SuppressWarnings("unchecked") + public static T StringToNumber(String snum, Class as) { + if (as == null) { + if (snum.contains(".")) + return (T) new Double(Double.parseDouble(snum)); + else + try { + return (T) new Integer(Integer.parseInt(snum)); + } catch (NumberFormatException nfe) { + return (T) new Long(Long.parseLong(snum)); + } + } else { + if (as == BigDecimal.class) + return as.cast(new BigDecimal(snum)); + else if (as == BigInteger.class) + return as.cast(new BigInteger(snum)); + else if (as == int.class || as == Integer.class) + return (T) new Integer(Integer.parseInt(snum)); + else if (as == long.class || as == Long.class) + return (T) new Long(Long.parseLong(snum)); + else if (as == float.class || as == Float.class) + return (T) new Float(Float.parseFloat(snum)); + else if (as == double.class || as == Double.class) + return (T) new Double(Double.parseDouble(snum)); + else + throw new NiHaoException("Casting type " + as.getName() + " not supported."); + } + } +} diff --git a/NiHao/src/nihao/util/FileUtil.java b/NiHao/src/nihao/util/FileUtil.java new file mode 100644 index 0000000..e5e8419 --- /dev/null +++ b/NiHao/src/nihao/util/FileUtil.java @@ -0,0 +1,695 @@ +package nihao.util; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.CharArrayReader; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; + +public class FileUtil { + + private static String tempDirPropertyName = "java.io.tmpdir"; + private static String NOT_FOUND = "NOT_FOUND"; + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + /** + * Escribe en un fichero un array de bytes + * + * @param fname + * Ruta dónde guardar los bytes + * @param data + * Bytes a guardar + */ + public static void bytesToFile(String fname, byte[] data) { + try { + FileOutputStream fos = new FileOutputStream(fname); + fos.write(data); + fos.close(); + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + /** + * Vuelca los datos de un archivo en un array de bytes + * + * @param fname + * Ruta del archivo a leer + * @return byte[] Los bytes del fichero + */ + public static byte[] fileToBytes(String fname) { + try { + FileInputStream fis = new FileInputStream(fname); + byte[] result = new byte[fis.available()]; + fis.read(result); + fis.close(); + return result; + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + /** + * Lee un archivo de la ruta temporal del S.O. + * + * @param filename + * nombre del archivo en la ruta temporal + * @return FileInputStream del archivo + */ + public static InputStream readTempFile(String filename) throws IOException, FileNotFoundException { + String tempPath = System.getProperty(tempDirPropertyName, NOT_FOUND); + if (tempPath.equals(NOT_FOUND)) + throw new RuntimeException("Temporary File not found"); + InputStream tmpFile = new FileInputStream(tempPath + System.getProperty("file.separator") + filename + ".crl"); + return tmpFile; + } + + /** + * + * @param src + * @param dest + * @param bufferSize + * @throws IOException + */ + public static void copyToTempPath(byte[] src, String filename, int bufferSize) throws IOException { + if (bufferSize <= 0) + bufferSize = 2000; + String tempPath = System.getProperty(tempDirPropertyName, NOT_FOUND); + if (tempPath.equals(NOT_FOUND)) + throw new RuntimeException("Temporary File not found"); + InputStream is = new ByteArrayInputStream(src); + OutputStream os = new BufferedOutputStream(new FileOutputStream(tempPath + System.getProperty("file.separator") + filename + ".crl")); + byte[] buffer = new byte[bufferSize]; + int c; + while ((c = is.read(buffer)) != -1) + os.write(buffer, 0, c); + is.close(); + os.close(); + return; + } + + // Writers + // ------------------------------------------------------------------------------------ + + /** + * Write byte array to file. If file already exists, it will be overwritten. + * + * @param file + * The file where the given byte array have to be written to. + * @param bytes + * The byte array which have to be written to the given file. + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, byte[] bytes) throws IOException { + write(file, new ByteArrayInputStream(bytes), false); + } + + /** + * Write byte array to file with option to append to file or not. If not, + * then any existing file will be overwritten. + * + * @param file + * The file where the given byte array have to be written to. + * @param bytes + * The byte array which have to be written to the given file. + * @param append + * Append to file? + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, byte[] bytes, boolean append) throws IOException { + write(file, new ByteArrayInputStream(bytes), append); + } + + /** + * Write byte inputstream to file. If file already exists, it will be + * overwritten.It's highly recommended to feed the inputstream as + * BufferedInputStream or ByteArrayInputStream as those are been + * automatically buffered. + * + * @param file + * The file where the given byte inputstream have to be written + * to. + * @param input + * The byte inputstream which have to be written to the given + * file. + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, InputStream input) throws IOException { + write(file, input, false); + } + + /** + * Write byte inputstream to file with option to append to file or not. If + * not, then any existing file will be overwritten. It's highly recommended + * to feed the inputstream as BufferedInputStream or ByteArrayInputStream as + * those are been automatically buffered. + * + * @param file + * The file where the given byte inputstream have to be written + * to. + * @param input + * The byte inputstream which have to be written to the given + * file. + * @param append + * Append to file? + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, InputStream input, boolean append) throws IOException { + mkdirs(file); + BufferedOutputStream output = null; + try { + output = new BufferedOutputStream(new FileOutputStream(file, append)); + int data = -1; + while ((data = input.read()) != -1) { + output.write(data); + } + } finally { + close(input, file); + close(output, file); + } + } + + /** + * Write character array to file. If file already exists, it will be + * overwritten. + * + * @param file + * The file where the given character array have to be written + * to. + * @param chars + * The character array which have to be written to the given + * file. + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, char[] chars) throws IOException { + write(file, new CharArrayReader(chars), false); + } + + /** + * Write character array to file with option to append to file or not. If + * not, then any existing file will be overwritten. + * + * @param file + * The file where the given character array have to be written + * to. + * @param chars + * The character array which have to be written to the given + * file. + * @param append + * Append to file? + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, char[] chars, boolean append) throws IOException { + write(file, new CharArrayReader(chars), append); + } + + /** + * Write string value to file. If file already exists, it will be + * overwritten. + * + * @param file + * The file where the given string value have to be written to. + * @param string + * The string value which have to be written to the given file. + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, String string) throws IOException { + write(file, new CharArrayReader(string.toCharArray()), false); + } + + /** + * Write string value to file with option to append to file or not. If not, + * then any existing file will be overwritten. + * + * @param file + * The file where the given string value have to be written to. + * @param string + * The string value which have to be written to the given file. + * @param append + * Append to file? + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, String string, boolean append) throws IOException { + write(file, new CharArrayReader(string.toCharArray()), append); + } + + /** + * Write character reader to file. If file already exists, it will be + * overwritten. It's highly recommended to feed the reader as BufferedReader + * or CharArrayReader as those are been automatically buffered. + * + * @param file + * The file where the given character reader have to be written + * to. + * @param reader + * The character reader which have to be written to the given + * file. + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, Reader reader) throws IOException { + write(file, reader, false); + } + + /** + * Write character reader to file with option to append to file or not. If + * not, then any existing file will be overwritten. It's highly recommended + * to feed the reader as BufferedReader or CharArrayReader as those are been + * automatically buffered. + * + * @param file + * The file where the given character reader have to be written + * to. + * @param reader + * The character reader which have to be written to the given + * file. + * @param append + * Append to file? + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, Reader reader, boolean append) throws IOException { + mkdirs(file); + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(file, append)); + int data = -1; + while ((data = reader.read()) != -1) { + writer.write(data); + } + } finally { + close(reader, file); + close(writer, file); + } + } + + /** + * Write list of String records to file. If file already exists, it will be + * overwritten. + * + * @param file + * The file where the given character reader have to be written + * to. + * @param records + * The list of String records which have to be written to the + * given file. + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, List records) throws IOException { + write(file, records, false); + } + + /** + * Write list of String records to file with option to append to file or + * not. If not, then any existing file will be overwritten. + * + * @param file + * The file where the given character reader have to be written + * to. + * @param records + * The list of String records which have to be written to the + * given file. + * @param append + * Append to file? + * @throws IOException + * If writing file fails. + */ + public static void write(java.io.File file, List records, boolean append) throws IOException { + mkdirs(file); + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(file, append)); + for (String record : records) { + writer.write(record); + writer.write(LINE_SEPARATOR); + } + } finally { + close(writer, file); + } + } + + // Readers + // ------------------------------------------------------------------------------------ + + /** + * Read byte array from file. Take care with big files, this would be memory + * hogging, rather use readStream() instead. + * + * @param file + * The file to read the byte array from. + * @return The byte array with the file contents. + * @throws IOException + * If reading file fails. + */ + public static byte[] readBytes(java.io.File file) throws IOException { + BufferedInputStream stream = (BufferedInputStream) readStream(file); + byte[] bytes = new byte[stream.available()]; + stream.read(bytes); + return bytes; + } + + /** + * Read byte stream from file. + * + * @param file + * The file to read the byte stream from. + * @return The byte stream with the file contents (actually: + * BufferedInputStream). + * @throws IOException + * If reading file fails. + */ + public static InputStream readStream(java.io.File file) throws IOException { + return new BufferedInputStream(new FileInputStream(file)); + } + + /** + * Read character array from file. Take care with big files, this would be + * memory hogging, rather use readReader() instead. + * + * @param file + * The file to read the character array from. + * @return The character array with the file contents. + * @throws IOException + * If reading file fails. + */ + public static char[] readChars(java.io.File file) throws IOException { + BufferedReader reader = (BufferedReader) readReader(file); + char[] chars = new char[(int) file.length()]; + reader.read(chars); + return chars; + } + + /** + * Read string value from file. Take care with big files, this would be + * memory hogging, rather use readReader() instead. + * + * @param file + * The file to read the string value from. + * @return The string value with the file contents. + * @throws IOException + * If reading file fails. + */ + public static String readString(java.io.File file) throws IOException { + return new String(readChars(file)); + } + + /** + * Read character reader from file. + * + * @param file + * The file to read the character reader from. + * @return The character reader with the file contents (actually: + * BufferedReader). + * @throws IOException + * If reading file fails. + */ + public static Reader readReader(java.io.File file) throws IOException { + return new BufferedReader(new FileReader(file)); + } + + /** + * Read list of String records from file. + * + * @param file + * The file to read the character writer from. + * @return A list of String records which represents lines of the file + * contents. + * @throws IOException + * If reading file fails. + */ + public static List readRecords(java.io.File file) throws IOException { + BufferedReader reader = (BufferedReader) readReader(file); + List records = new ArrayList(); + String record = null; + try { + while ((record = reader.readLine()) != null) + records.add(record); + } finally { + close(reader, file); + } + return records; + } + + // Copiers + // ------------------------------------------------------------------------------------ + + /** + * Copy file. Any existing file at the destination will be overwritten. + * + * @param source + * The file to read the contents from. + * @param destination + * The file to write the contents to. + * @throws IOException + * If copying file fails. + */ + public static void copy(java.io.File source, java.io.File destination) throws IOException { + copy(source, destination, true); + } + + /** + * Copy file with the option to overwrite any existing file at the + * destination. + * + * @param source + * The file to read the contents from. + * @param destination + * The file to write the contents to. + * @param overwrite + * Set whether to overwrite any existing file at the destination. + * @throws IOException + * If the destination file already exists while + * overwrite is set to false, or if copying file fails. + */ + public static void copy(java.io.File source, java.io.File destination, boolean overwrite) { + if (destination.exists() && !overwrite) + throw new RuntimeException("Copying file " + source.getPath() + " to " + destination.getPath() + " failed." + " The destination file already exists."); + try { + mkdirs(destination); + BufferedInputStream input = null; + BufferedOutputStream output = null; + + try { + input = new BufferedInputStream(new FileInputStream(source)); + output = new BufferedOutputStream(new FileOutputStream(destination)); + int data = -1; + while ((data = input.read()) != -1) { + output.write(data); + } + } finally { + close(input, source); + close(output, destination); + } + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + // Movers + // ------------------------------------------------------------------------------------- + + /** + * Move (rename) file. Any existing file at the destination will be + * overwritten. + * + * @param source + * The file to be moved. + * @param destination + * The new destination of the file. + * @throws IOException + * If moving file fails. + */ + public static void move(java.io.File source, java.io.File destination) throws IOException { + move(source, destination, true); + } + + /** + * Move (rename) file with the option to overwrite any existing file at the + * destination. + * + * @param source + * The file to be moved. + * @param destination + * The new destination of the file. + * @param overwrite + * Set whether to overwrite any existing file at the destination. + * @throws IOException + * If the destination file already exists while + * overwrite is set to false, or if moving file fails. + */ + public static void move(java.io.File source, java.io.File destination, boolean overwrite) { + if (destination.exists()) { + if (overwrite) { + destination.delete(); + } else { + throw new RuntimeException("Moving file " + source.getPath() + " to " + destination.getPath() + " failed." + " The destination file already exists."); + } + } + try { + mkdirs(destination); + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + if (!source.renameTo(destination)) { + throw new RuntimeException("Moving file " + source.getPath() + " to " + destination.getPath() + " failed."); + } + } + + // Filenames + // ---------------------------------------------------------------------------------- + + /** + * Trim the eventual file path from the given file name. Anything before the + * last occurred "/" and "\" will be trimmed, including the slash. + * + * @param fileName + * The file name to trim the file path from. + * @return The file name with the file path trimmed. + */ + public static String trimFilePath(String fileName) { + return fileName.substring(fileName.lastIndexOf("/") + 1).substring(fileName.lastIndexOf("\\") + 1); + } + + /** + * Generate unique file based on the given path and name. If the file + * exists, then it will add "[i]" to the file name as long as the file + * exists. The value of i can be between 0 and 2147483647 (the value of + * Integer.MAX_VALUE). + * + * @param filePath + * The path of the unique file. + * @param fileName + * The name of the unique file. + * @return The unique file. + * @throws IOException + * If unique file cannot be generated, this can be caused if all + * file names are already in use. You may consider another + * filename instead. + */ + public static java.io.File uniqueFile(java.io.File filePath, String fileName) throws IOException { + java.io.File file = new java.io.File(filePath, fileName); + + if (file.exists()) { + + // Split filename and add braces, e.g. "name.ext" --> "name[", + // "].ext". + String prefix; + String suffix; + int dotIndex = fileName.lastIndexOf("."); + + if (dotIndex > -1) { + prefix = fileName.substring(0, dotIndex) + "["; + suffix = "]" + fileName.substring(dotIndex); + } else { + prefix = fileName + "["; + suffix = "]"; + } + + int count = 0; + + // Add counter to filename as long as file exists. + while (file.exists()) { + if (count < 0) { // int++ restarts at -2147483648 after + // 2147483647. + throw new IOException("No unique filename available for " + fileName + " in path " + filePath.getPath() + "."); + } + + // Glue counter between prefix and suffix, e.g. "name[" + count + // + "].ext". + file = new java.io.File(filePath, prefix + (count++) + suffix); + } + } + + return file; + } + + // Helpers + // ------------------------------------------------------------------------------------ + + /** + * Check and create missing parent directories for the given file. + * + * @param file + * The file to check and create the missing parent directories + * for. + * @throws IOException + * If the given file is actually not a file or if creating + * parent directories fails. + */ + private static void mkdirs(java.io.File file) throws IOException { + if (file.exists() && !file.isFile()) { + throw new IOException("File " + file.getPath() + " is actually not a file."); + } + java.io.File parentFile = file.getParentFile(); + if (!parentFile.exists() && !parentFile.mkdirs()) { + throw new IOException("Creating directories " + parentFile.getPath() + " failed."); + } + } + + /** + * Close the given I/O resource of the given file. + * + * @param resource + * The I/O resource to be closed. + * @param file + * The I/O resource's subject. + */ + private static void close(Closeable resource, java.io.File file) { + if (resource != null) { + try { + resource.close(); + } catch (IOException e) { + String message = "Closing file " + file.getPath() + " failed."; + // Do your thing with the exception and the message. Print it, + // log it or mail it. + System.err.println(message); + e.printStackTrace(); + } + } + } + + /** + * Crea una estructura de directorios con la ruta que le entra + * + * @param dirpath + * @return true si ha conseguido crear los directorios/false si no ha podido + * crearlo + */ + public static boolean createDir(String dirpath) { + File file = new File(dirpath); + if (file.exists()) + return true; + return file.mkdirs(); + } +} diff --git a/NiHao/src/nihao/util/HM.java b/NiHao/src/nihao/util/HM.java new file mode 100644 index 0000000..3224e8a --- /dev/null +++ b/NiHao/src/nihao/util/HM.java @@ -0,0 +1,26 @@ +package nihao.util; + +import java.util.HashMap; + +/** + * Fast HashMap Building Class + * + * @author XWolf Override + * + */ +public class HM { + private HashMap data; + + public HM() { + data = new HashMap(); + } + + public HM put(K k, V v) { + data.put(k, v); + return this; + } + + public HashMap get() { + return data; + } +} diff --git a/NiHao/src/nihao/util/HashType.java b/NiHao/src/nihao/util/HashType.java new file mode 100644 index 0000000..7af1698 --- /dev/null +++ b/NiHao/src/nihao/util/HashType.java @@ -0,0 +1,44 @@ +package nihao.util; + +public enum HashType { + + SHA1("SHA-1", "1.3.14.3.2.26"), SHA256("SHA-256","2.16.840.1.101.3.4.2.1"), SHA384("SHA-384","2.16.840.1.101.3.4.2.2"), SHA512("SHA-512","2.16.840.1.101.3.4.2.3"), SHA224("SHA-224","2.16.840.1.101.3.4.2.4"), MD5("MD5", "1.2.840.113549.2.5"), MD2("MD2","1.2.840.113549.2.2"); + +// static final String digestAlgorithm = "1.2.840.113549.2"; +// static final String md2 = digestAlgorithm + ".2"; +// static final String md5 = digestAlgorithm + ".5"; +// +// static final String nistAlgorithm = "2.16.840.1.101.3.4"; +// static final String id_sha256 = nistAlgorithm + ".2.1"; +// static final String id_sha384 = nistAlgorithm + ".2.2"; +// static final String id_sha512 = nistAlgorithm + ".2.3"; +// static final String id_sha224 = nistAlgorithm + ".2.4"; + + private HashType(String v, String oid) { + value = v; + this.oid = oid; + } + + private String value; + private String oid; + + public String getValue() { + return value; + } + + public String getOID() { + return oid; + } + + /** + * Retorna un HashType desde su OID, si no lo encuentra retorna null + * @param oid String + * @return HashType + */ + public static HashType fromOID(String oid){ + for(HashType t:values()) + if (t.oid.equals(oid)) + return t; + return null; + } +} diff --git a/NiHao/src/nihao/util/Hasher.java b/NiHao/src/nihao/util/Hasher.java new file mode 100644 index 0000000..802baef --- /dev/null +++ b/NiHao/src/nihao/util/Hasher.java @@ -0,0 +1,162 @@ +package nihao.util; + +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +/** + * Herramientas con datos + * + * @author ivan.dominguez + * + */ +public class Hasher { + /** + * Genera el SHA-1 de un array de bytes + * + * @param data + * Array de bytes + * @return Array de bytes con el digest + */ + public static byte[] SHA1(byte[] data) { + return generateHash(data, HashType.SHA1); + } + + /** + * Genera el SHA-1 de un array de un inputStream (el input stream se lee + * hasta el final) + * + * @param is + * InputStream con los bytes + * @return byte[] con el SHA1 + */ + public static byte[] SHA1(InputStream is) { + return generateHash(is, HashType.SHA1); + } + + /** + * Genera un Hash de un array de bytes + * + * @param data + * Array de bytes + * @param hash + * HashType con el tipo de Hash a aplicar + * @return digest del hashing + */ + public static byte[] generateHash(byte[] data, HashType hash) { + try { + MessageDigest md; + md = MessageDigest.getInstance(hashTypeToString(hash)); + md.update(data); + return md.digest(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + /** + * Genera el hash de un array de un inputStream (el input stream se lee + * hasta el final) + * + * @param is + * InputStream con los bytes + * @param hash + * HashType con el tipo de hash a realizar + * @return byte[] con el hash + */ + public static byte[] generateHash(InputStream is, HashType hash) { + try { + MessageDigest md; + md = MessageDigest.getInstance(hashTypeToString(hash)); + byte buf[] = new byte[1024]; + int n; + while ((n = is.read(buf)) > 0) + md.update(buf, 0, n); + return md.digest(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Genera un Hash de un array de bytes + * + * @param data + * Array de bytes + * @param algorithm + * Sting con el nombre MessageDigest del algoritmo + * @return Digest + * @throws NoSuchAlgorithmException + * el string del nombre de hash es incorrecto + */ + public static byte[] generateHash(byte[] data, String algorithm) throws NoSuchAlgorithmException { + MessageDigest md; + md = MessageDigest.getInstance(algorithm); + md.update(data); + return md.digest(); + } + + /** + * Retorna el nombre de algoritmo MessageDigest a partir de un HashType + * + * @param hash + * tipo de hash + * @return Nombre del algoritmo. + */ + public static String hashTypeToString(HashType hash) { + switch (hash) { + case SHA1: + return "SHA-1"; + case MD5: + return "MD5"; + case MD2: + return "MD2"; + case SHA224: + return "SHA-224"; + case SHA256: + return "SHA-256"; + case SHA384: + return "SHA-384"; + case SHA512: + return "SHA-512"; + default: + throw new RuntimeException("Unimplemented hash ''" + hash.name() + "'' @hashTypeToString"); + } + } + + /** + * Encripta datos con una clave publica en RSA con BouncyCastle + * + * @param inpBytes + * bytes a cifrar + * @param key + * clave publica + * @return bytes encriptados + * @throws NoSuchPaddingException + * @throws InvalidKeyException + * @throws IllegalBlockSizeException + * @throws BadPaddingException + */ + public static byte[] encrypt(byte[] inpBytes, PublicKey key) throws NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + try { + Cipher cipher = Cipher.getInstance("RSA", "BC"); + cipher.init(Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(inpBytes); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (NoSuchProviderException e) { + throw new RuntimeException(e); + } + } +} diff --git a/NiHao/src/nihao/util/IoUtil.java b/NiHao/src/nihao/util/IoUtil.java new file mode 100644 index 0000000..a1a95e2 --- /dev/null +++ b/NiHao/src/nihao/util/IoUtil.java @@ -0,0 +1,199 @@ +package nihao.util; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; + +import nihao.NiHaoException; + +public class IoUtil { + /** + * Lee un stream hasta el final y lo retorna en forma de byte[] + * + * @param inputStream + * InputStream de donde leer + * @return byte[] + */ + public static byte[] inputStreamToByteArray(InputStream inputStream) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int l; + byte[] tmp = new byte[8192]; + while ((l = inputStream.read(tmp)) != -1) + baos.write(tmp, 0, l); + baos.close(); + return baos.toByteArray(); + } catch (NiHaoException e) { + throw e; + } catch (IOException e) { + throw new NiHaoException(e); + } + } + + /** + * Escribe un byte[] en bloques de hasta 255 bytes, cada uno con su marca de + * tama�o.
+ * Este m�todo no es aconsejable para archivos grandes, ya que genera un + * byte por cada 255 bytes (4Kb por cada Mb).
+ * Pero se recomienda para archivos de entre 0 y 5 Mb, y en escrituras de + * streams de red (omitiendo falsos finales)
+ * Para archivos mayores usar writeLongBlock + * + * @param data + * byte[] con los datos + * @param os + * OutputStream destino + */ + public static void writeChunkedBlockToStream(byte[] data, OutputStream os) { + int len = data.length; + int pos = 0; + try { + while (pos < len) { + int chunklen = Math.min(len - pos, 255); + os.write(chunklen); + os.write(data, pos, chunklen); + pos += chunklen; + } + } catch (NiHaoException e) { + throw e; + } catch (IOException e) { + throw new NiHaoException(e); + } + } + + /** + * Lee un byte[] desde un InputStream, usando el m�todo "ChunckedBlock" + * definido en writeChunkedBlockToStream
+ * + * @param is + * InputStream de origen + * @return byte[] con el bloque. + */ + public static byte[] readChunkedBlockFromStream(InputStream is) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[255]; + try { + int chunklen; + do { + chunklen = is.read(); + if (chunklen != is.read(buffer, 0, chunklen)) + throw new NiHaoException("Unexpected stream EOF"); + baos.write(buffer, 0, chunklen); + } while (chunklen < 255); + baos.close(); + return baos.toByteArray(); + } catch (NiHaoException e) { + throw e; + } catch (IOException e) { + throw new NiHaoException(e); + } + } + + /** + * Copia un numero determinado de bytes de un Stream a otro + * + * @param from + * InputStream origen + * @param to + * OutputStream destino + * @param length + * cantidad a copiar + */ + public static int copyStream(InputStream from, OutputStream to, int length) { + try { + int result = 0; + byte[] tmp = new byte[8192]; + while (result < length) { + int blockLength = Math.min(tmp.length, length - result); + int readed = from.read(tmp); + to.write(tmp, 0, readed); + result += readed; + if (readed != blockLength) + break; + } + return result; + } catch (NiHaoException e) { + throw e; + } catch (IOException e) { + throw new NiHaoException(e); + } + } + + /** + * Copia los datos de un stream a otro hasta el final del origen + * + * @param from + * InputStream origen + * @param to + * OutputStream destino + */ + public static void copyStream(InputStream from, OutputStream to) { + try { + int l; + byte[] tmp = new byte[8192]; + while ((l = from.read(tmp)) != -1) + to.write(tmp, 0, l); + } catch (NiHaoException e) { + throw e; + } catch (IOException e) { + throw new NiHaoException(e); + } + } + + /** + * Lee un stream hasta que termina y lo retorna como array de bytes + * + * @param is + * InputStream + * @return byte[] + */ + public static byte[] readToEnd(InputStream is) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int l; + byte[] tmp = new byte[2048]; + while ((l = is.read(tmp)) != -1) + baos.write(tmp, 0, l); + return baos.toByteArray(); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Reads an InputStream and returns its contents as String + * + * @param is + * InputStream + * @return String + */ + public static String readStringToEnd(InputStream is) { + return readStringToEnd(new BufferedReader(new InputStreamReader(is))); + } + + /** + * Reads a BufferedReader and returns its contents as String + * + * @param br + * BufferedReader + * @return String + */ + public static String readStringToEnd(BufferedReader br) { + try { + char[] charBuffer = new char[512]; + int readed = -1; + StringBuilder sb = new StringBuilder(); + while ((readed = br.read(charBuffer)) > 0) + sb.append(charBuffer, 0, readed); + return sb.toString(); + } catch (IOException e) { + throw new NiHaoException(e); + } + } + +} diff --git a/NiHao/src/nihao/util/LRUCache.java b/NiHao/src/nihao/util/LRUCache.java new file mode 100644 index 0000000..d6b0c67 --- /dev/null +++ b/NiHao/src/nihao/util/LRUCache.java @@ -0,0 +1,21 @@ +package nihao.util; + +import java.util.LinkedHashMap; +import java.util.Map; + + +public class LRUCache extends LinkedHashMap { + private static final long serialVersionUID = 1L; + + private final int limit; + + public LRUCache(int limit) { + super(16, 0.75f, true); + this.limit = limit; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > limit; + } +} \ No newline at end of file diff --git a/NiHao/src/nihao/util/QDB.java b/NiHao/src/nihao/util/QDB.java new file mode 100644 index 0000000..4fe993b --- /dev/null +++ b/NiHao/src/nihao/util/QDB.java @@ -0,0 +1,58 @@ +package nihao.util; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map.Entry; + +public class QDB { + private HashMap childs = new HashMap(); + private Object data; + + private boolean empty() { + if (data != null) + return false; + for (QDB q : childs.values()) + if (!q.empty()) + return false; + return true; + } + + public QDB ₪(String key) { + QDB child = childs.get(key); + if (child == null) { + child = new QDB(); + childs.put(key, child); + } + return child; + } + + public QDB $(String key) { + QDB child = childs.get(key); + if (child == null) { + child = new QDB(); + childs.put(key, child); + } + return child; + } + + public Iterator iterator(){ + return childs.values().iterator(); + } + + public void set(Object data) { + this.data = data; + } + + public T get() { + return Conversor.as(data); + } + + public void purge() { + for (Entry e : childs.entrySet()) { + if (e.getValue().empty()) + childs.remove(e.getKey()); + else + e.getValue().purge(); + } + } +} diff --git a/NiHao/src/nihao/util/Resources.java b/NiHao/src/nihao/util/Resources.java new file mode 100644 index 0000000..a1d3251 --- /dev/null +++ b/NiHao/src/nihao/util/Resources.java @@ -0,0 +1,162 @@ +package nihao.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; + +public class Resources { + public static ArrayList clsloaders = new ArrayList(); + public static ArrayList resourcespath = new ArrayList(); + + static { + addClassLoader(Resources.class.getClassLoader()); + addClassLoader(InputStream.class.getClassLoader()); + addClassLoader(Thread.currentThread().getContextClassLoader()); + } + + /** + * Accede a recursos locales en el siguiente orden: Recursos del + * SystemClassLoader, Recursos del ClassLoader actual, Recursos del sistema + * de archivos, uno a uno los distintos ClassLoaders almacenados, uno a uno + * las distintas rutas de recursos. + */ + public static InputStream getResourceAsStream(String path) { + try { + return new FileInputStream(path); + } catch (Exception e) { + } + ; + InputStream result; + result = ClassLoader.getSystemResourceAsStream(path); + if (result != null) + return result; + result = getResourceAsStreamInResourcesClassLoaders(path); + if (result != null) + return result; + result = getResourceAsStreamInResourcesPaths(path); + return result; + } + + /** + * Retorna un recurso buscandolo en los RsourceClassLoaders + * + * @param path + * ruta del archivo + * @return InputStream con el archivo, o null si no se encuentra + */ + public static InputStream getResourceAsStreamInResourcesClassLoaders(String path) { + for (ClassLoader cls : clsloaders) { + InputStream result = cls.getResourceAsStream(path); + if (result != null) + return result; + } + return null; + } + + /** + * Busca el recurso entre las rutas de recursos definidas y lo retorna + * + * @param path + * ruta relativa + * @return InputStream del archivo, o null si no se encontro + */ + public static InputStream getResourceAsStreamInResourcesPaths(String path) { + InputStream result; + for (String p : resourcespath) { + try { + result = new FileInputStream(p + path); + return result; + } catch (Exception e) { + } + ; + } + return null; + } + + /** + * Busca el recurso en un ClassLoader especifico, si no lo encuentra, lo + * busca usando el orden de getResourceAsStream + * + * @param cl + * @param path + * @return + */ + public static InputStream getResourceAsStreamFromClassLoader(ClassLoader cl, String path) { + InputStream result; + result = cl.getResourceAsStream(path); + if (result != null) + return result; + return getResourceAsStream(path); + } + + public static String getResourcePath(String resourceName) { + URL path = ClassLoader.getSystemResource(resourceName); + if (path == null) { + File f = new File(resourceName); + if (f.exists()) + return f.getAbsolutePath(); + path = getResourcePathInResourcesClassLoaders(resourceName); + } + return UrlToPath(path); + } + + private static String UrlToPath(URL url) { + String result = url.getPath(); + return result.substring(1).replaceAll("%20", " "); + } + + private static URL getResourcePathInResourcesClassLoaders(String path) { + for (ClassLoader cls : clsloaders) { + URL result = cls.getResource(path); + if (result != null) + return result; + } + return null; + } + + /** + * A�ade un classLoader a la lista de b�squeda + * + * @param c + * ClassLoader + * @return true si se a�adio, no se a�aden duplicados + */ + public static boolean addClassLoader(ClassLoader c) { + if (c == null) + return false; + if (!clsloaders.contains(c)) { + clsloaders.add(c); + return true; + } + return false; + } + + /** + * Añade una ruta de recursos si no se a�adio anteriormente + * + * @param path + * ruta local + * @return true se se añadió + */ + public static boolean addResourcePath(String path) { + if (!path.endsWith("\\")) + path += '\\'; + for (String s : resourcespath) { + if (s.equals(path)) + return false; + } + resourcespath.add(path); + return true; + } + + /** + * Return all the registered (and known) class loaders + * @return + */ + public static ClassLoader[] getClassLoaders() { + return clsloaders.toArray(new ClassLoader[clsloaders.size()]); + } + +} diff --git a/NiHao/src/nihao/util/Serializer.java b/NiHao/src/nihao/util/Serializer.java new file mode 100644 index 0000000..0f397f6 --- /dev/null +++ b/NiHao/src/nihao/util/Serializer.java @@ -0,0 +1,450 @@ +package nihao.util; + +import java.beans.XMLDecoder; +import java.beans.XMLEncoder; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map.Entry; + +import nihao.NiHaoException; +import nihao.tokenizer.QToken; +import nihao.tokenizer.QTokenType; +import nihao.tokenizer.QTokenizer; +import nihao.util.reflection.Reflector; + +/** + * Serializa objetos + * + * @author ivan.dominguez + * + */ +public class Serializer { + final static String TextEncoding = "UTF-8"; + + /** + * Serializa un objeto a un Stream + * + * @param o + * Objeto serializable + * @param os + * OutputStream destino + */ + public static void serialize(Serializable o, OutputStream os) { + try { + ObjectOutputStream oos = new ObjectOutputStream(os); + oos.writeObject(o); + oos.flush(); + } catch (NiHaoException e) { + throw e; + } catch (IOException e) { + throw new NiHaoException(e); + } + } + + /** + * Serializa un objeto a un array de bytes + * + * @param o + * Objecto serializable + * @return Array de bytes con el contenido + */ + + public static byte[] serialize(Serializable o) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serialize(o, baos); + baos.close(); + return baos.toByteArray(); + } catch (NiHaoException e) { + throw e; + } catch (IOException e) { + throw new NiHaoException(e); + } + } + + /** + * Serializa un objeto a un stream, en fomrato "ChunkedBlocks" definido por + * core.io.IoTools.writeChunkedBlockToStream
+ * Este m�todo usa un paso por RAM del objeto serializado, pudiendo causar + * un alto uso de esta en serialiaciones grandes. + * + * @param o + * Objeto serializable + * @param os + * OutputStream + */ + public static void serializeChunckedBlock(Serializable o, OutputStream os) { + IoUtil.writeChunkedBlockToStream(serialize(o), os); + } + + /** + * Serializa un array de objetos a un array de bytes + * + * @param a + * Array de objetos + * @return Array de bytes + * @throws IOException + */ + public static byte[] serializeArray(Object[] a) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(a.getClass().getComponentType()); + oos.writeInt(a.length); + for (Object o : a) { + oos.writeObject(o); + } + oos.close(); + byte[] result = baos.toByteArray(); + baos.close(); + return result; + } catch (NiHaoException e) { + throw e; + } catch (IOException e) { + throw new NiHaoException(e); + } + } + + /** + * Deserializa a partir de un stream + * + * @param inputStream + * InputStream con el serializado + * @return Serializable + */ + public static Serializable deserialize(InputStream inputStream) { + try { + ObjectInputStream ois = new ObjectInputStream(inputStream); + return (Serializable) ois.readObject(); + } catch (IOException e) { + throw new NiHaoException(e); + } catch (ClassNotFoundException e) { + throw new NiHaoException(e); + } + } + + public static T deserialize(InputStream inputStream, Class resultType) { + Serializable o = deserialize(inputStream); + if (o == null) + return null; + if (resultType.isAssignableFrom(o.getClass())) + return null; + return resultType.cast(o); + } + + /** + * Deserializa a partir de un array de bytes + * + * @param data + * Array de bytes + * @return Objet + * @throws IOException + * @throws ClassNotFoundException + */ + public static Serializable deserialize(byte[] data) { + try { + ByteArrayInputStream bais = new ByteArrayInputStream(data); + Serializable result = deserialize(bais); + bais.close(); + return result; + } catch (IOException e) { + throw new NiHaoException(e); + } + } + + public static T deserialize(byte[] data, Class resultType) { + Serializable o = deserialize(data); + if (o == null) + return null; + if (resultType.isAssignableFrom(o.getClass())) + return null; + return resultType.cast(o); + } + + /** + * Deserializa un array desde un array de bytes + * + * @param data + * Array de bytes + * @return Array de objetos + * @throws IOException + * @throws ClassNotFoundException + */ + @SuppressWarnings("unchecked") + public static T[] deserializeArray(byte[] data) { + try { + ByteArrayInputStream bais = new ByteArrayInputStream(data); + ObjectInputStream ois = new ObjectInputStream(bais); + Class aType = (Class) ois.readObject(); + int aLength = ois.readInt(); + T[] result = (T[]) Array.newInstance(aType, aLength); + for (int i = 0; i < aLength; i++) { + result[i] = (T) ois.readObject(); + } + ois.close(); + bais.close(); + return result; + } catch (IOException e) { + throw new NiHaoException(e); + } catch (ClassNotFoundException e) { + throw new NiHaoException(e); + } + } + + /** + * Serializa un objeto a un XML + * + * @param o + * objeto + * @return String con el XML + */ + public static String serializeToXML(Object o) { + return serializeToXML(o, TextEncoding); + } + + /** + * Serializa un objeto a un XML con un encoding + * + * @param o + * Objeto + * @param encoding + * Texto del encoding + * @return String con el XML + */ + public static String serializeToXML(Object o, String encoding) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + XMLEncoder xmle = new XMLEncoder(baos); + xmle.writeObject(o); + xmle.close(); + try { + return new String(baos.toByteArray(), encoding); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unsupported charset: " + encoding, e); + } + } + + /** + * Deserializa desde un XML + * + * @param xml + * XML con el objeto + * @return Objeto + */ + public static Object deserializeFromXML(String xml) { + return deserializeFromXML(xml, TextEncoding); + } + + /** + * Deserializa desde un XML con un enconding especifico + * + * @param xml + * XML + * @param encoding + * Código de encoding + * @return Objeto + */ + public static Object deserializeFromXML(String xml, String encoding) { + ByteArrayInputStream bais; + try { + bais = new ByteArrayInputStream(xml.getBytes(encoding)); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unsupported charset: " + encoding, e); + } + XMLDecoder xmld = new XMLDecoder(bais); + Object result = xmld.readObject(); + xmld.close(); + return result; + } + + /** + * Deserializes from any object to a JSON string. Only public geters and + * seters are used + * + * @param data + * Object bean. + * @return String JSON + */ + public static String serializeToJSON(Object data) { + StringBuilder sb = new StringBuilder(); + serializeToJSON(data, sb); + return sb.toString(); + } + + private static void serializeToJSON(Object data, StringBuilder dest) { + if (data == null) { + dest.append(""); + } else if (data instanceof String) { + dest.append("\""); + dest.append(jsStringEncode((String) data)); + dest.append("\""); + } else { + Class c = data.getClass(); + if (c.isPrimitive() || data instanceof BigInteger || data instanceof BigDecimal || data instanceof Integer || data instanceof Long || data instanceof Float || data instanceof Double) { + dest.append(data); + } else if (c.isArray() || data instanceof Collection) { + dest.append('['); + boolean first = true; + if (c.isArray()) { + int len = Array.getLength(data); + ArrayList list = new ArrayList(len); + for (int i = 0; i < len; i++) + list.add(Array.get(data, i)); + data = list; + } + for (Object o : (Collection) data) { + if (first) + first = false; + else + dest.append(", "); + serializeToJSON(o, dest); + } + dest.append(']'); + } else { + dest.append('{'); + HashMap hm = data instanceof HashMap ? (HashMap) data : Reflector.getHashMapFromObject(data); + boolean first = true; + for (Entry e : hm.entrySet()) { + String key = e.getKey().toString(); + Object val = e.getValue(); + if (val == null) + continue; + if (first) + first = false; + else + dest.append(", "); + dest.append("\""); + dest.append(jsStringEncode(key)); + dest.append("\":"); + serializeToJSON(val, dest); + } + dest.append('}'); + } + } + } + + private static String jsStringEncode(String s) { + return s.replace("\"", "\\\""); + } + + /** + * Deserializes from JSON to a:
+ * · String if the JSON contains single String.
+ * · Integer or Double if the JSON contains single number.
+ * · Object[] if the JSON contains an array.
+ * · HashMap for complex objects.
+ * For typed conversions use deserializeFromJSON(String,type) + * + * @param json + * String JSON + * @return Object + */ + public static Object deserializeFromJSON(String json) { + return deserializeFromJSON(new QTokenizer(json), null, null); + } + + /** + * Deserializes from JSON to a desired type if compatible + * + * @param json + * String JSON + * @param as + * Class of object + * @param typeMap + * HashMap of classes or other typeMap to inform of the type tree + * @return Object + */ + public static T deserializeFromJSON(String json, Class as, HashMap typeMap) { + if (json.length() == 0) + return null; + return deserializeFromJSON(new QTokenizer(json), as, typeMap); + } + + @SuppressWarnings({ "unchecked" }) + private static T deserializeFromJSON(QTokenizer tkz, Class as, HashMap typeMap) { + QToken t = tkz.next(); + switch (t.getType()) { + case LITERAL: + return as == null ? (T) t.getValue() : as.cast(t.getValue()); + case NUMBER: + return Conversor.StringToNumber(t.getValue(), as); + case SYMBOL: + if ("{".equals(t.getValue())) { + T o = null; + if (as == null) + o = (T) new HashMap(); + else + try { + o = as.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new NiHaoException(e); + } + t = tkz.next(); + while (!"}".equals(t.getValue())) { + if (t.getType() != QTokenType.LITERAL && t.getType() != QTokenType.WORD) + throw new NiHaoException("JSON type error: " + t.getValue()); + String key = t.getValue(); + Class vcls; + Method m = null; + if (typeMap != null && typeMap.get(key) instanceof Class) + vcls = (Class) typeMap.get(key); + else if (as == null) + vcls = null; + else { + m = Reflector.getSetter(as, key); + vcls = m == null ? null : m.getParameterTypes()[0]; + } + if (!":".equals(tkz.next().getValue())) + throw new NiHaoException("JSON separator error: " + t.getValue()); + HashMap subTypeMap = typeMap != null && typeMap.get(key) instanceof HashMap ? (HashMap) typeMap.get(key) : null; + Object val = deserializeFromJSON(tkz, vcls, subTypeMap); + if (as == null || o instanceof HashMap) + ((HashMap) o).put(key, val); + else + try { + m.invoke(o, val); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new NiHaoException(e); + } + t = tkz.next(); + if (",".equals(t.getValue())) + t = tkz.next(); + } + return o; + } else if ("[".equals(t.getValue())) { + ArrayList buff = new ArrayList(); + Class vcls = as == null ? null : as.getComponentType(); + t = tkz.patrol(); + while (!"]".equals(t.getValue())) { + buff.add(deserializeFromJSON(tkz, vcls, typeMap)); + t = tkz.next(); + if (",".equals(t.getValue())) + t = tkz.patrol(); + } + t = tkz.next(); + if (as == null) + return (T) Conversor.toArray(buff, Object.class); + else if (as.isArray()) + return (T) Conversor.toArray(buff, vcls); + else + return (T) buff; + } + default: + throw new NiHaoException("JSON error: " + t.getValue()); + } + } +} diff --git a/NiHao/src/nihao/util/TextEncoding.java b/NiHao/src/nihao/util/TextEncoding.java new file mode 100644 index 0000000..014583f --- /dev/null +++ b/NiHao/src/nihao/util/TextEncoding.java @@ -0,0 +1,38 @@ +package nihao.util; + +public enum TextEncoding { + /** + * Desconocido + */ + Unknown, + + /** + * ASCII (usado tambi�n para ANSI) + */ + ASCII, + + /** + * UTF-8 + */ + UTF8, + + /** + * UTF-16 (UNICODE) Little Endian + */ + UTF16LE, + + /** + * UTF-16 (UNICODE) Big Endian + */ + UTF16BE, + + /** + * UTF-32 (UNICODE) Little Endian + */ + UTF32LE, + + /** + * UTF-32 (UNICODE) Big Endian + */ + UTF32BE, +} diff --git a/NiHao/src/nihao/util/cursor/ArrayCursor.java b/NiHao/src/nihao/util/cursor/ArrayCursor.java new file mode 100644 index 0000000..3ea9d27 --- /dev/null +++ b/NiHao/src/nihao/util/cursor/ArrayCursor.java @@ -0,0 +1,45 @@ +package nihao.util.cursor; + +import java.lang.reflect.Array; + +public class ArrayCursor extends Cursor { + + private int index = 0; + private Object array; + private int length; + + public ArrayCursor(Object array) { + this.array = array; + length = Array.getLength(array); + } + + @Override + public boolean canGoFirst() { + return true; + } + + @Override + public boolean canGetLength() { + return true; + } + + @Override + public Object next() { + return Array.get(array, index++); + } + + @Override + public boolean hasMoreElements() { + return index < length; + } + + @Override + public void goFirst() { + index = 0; + } + + @Override + public int getLength() { + return length; + } +} diff --git a/NiHao/src/nihao/util/cursor/Cursor.java b/NiHao/src/nihao/util/cursor/Cursor.java new file mode 100644 index 0000000..8c0480e --- /dev/null +++ b/NiHao/src/nihao/util/cursor/Cursor.java @@ -0,0 +1,31 @@ +package nihao.util.cursor; + +import java.util.Iterator; +import java.util.List; + +import nihao.NiHaoException; + +public abstract class Cursor { + public static Cursor getCursor(Object o) { + if (o == null) + return new ArrayCursor(new Object[0]); + if (o.getClass().isArray()) + return new ArrayCursor(o); + if (o instanceof List) + return new ListCursor((List) o); + if (o instanceof Iterator) + return new IteratorCursor((Iterator) o); + if (o instanceof Iterable) + return new IteratorCursor(((Iterable) o).iterator()); + throw new NiHaoException("Cursor unknown type " + o.getClass().getName()); + } + + public abstract boolean canGoFirst(); + public abstract boolean canGetLength(); + + public abstract Object next(); + public abstract boolean hasMoreElements(); + + public abstract void goFirst(); + public abstract int getLength(); +} diff --git a/NiHao/src/nihao/util/cursor/IteratorCursor.java b/NiHao/src/nihao/util/cursor/IteratorCursor.java new file mode 100644 index 0000000..fb052bc --- /dev/null +++ b/NiHao/src/nihao/util/cursor/IteratorCursor.java @@ -0,0 +1,41 @@ +package nihao.util.cursor; + +import java.util.Iterator; + +public class IteratorCursor extends Cursor { + + private Iterator i; + + public IteratorCursor(Iterator i) { + this.i = i; + } + + @Override + public boolean canGoFirst() { + return false; + } + + @Override + public boolean canGetLength() { + return false; + } + + @Override + public Object next() { + return i.next(); + } + + @Override + public boolean hasMoreElements() { + return i.hasNext(); + } + + @Override + public void goFirst() { + } + + @Override + public int getLength() { + return -1; + } +} diff --git a/NiHao/src/nihao/util/cursor/ListCursor.java b/NiHao/src/nihao/util/cursor/ListCursor.java new file mode 100644 index 0000000..c9087b8 --- /dev/null +++ b/NiHao/src/nihao/util/cursor/ListCursor.java @@ -0,0 +1,43 @@ +package nihao.util.cursor; + +import java.util.List; + +public class ListCursor extends Cursor { + + private int index = 0; + private List lst; + + public ListCursor(List lst) { + this.lst = lst; + } + + @Override + public boolean canGoFirst() { + return true; + } + + @Override + public boolean canGetLength() { + return true; + } + + @Override + public Object next() { + return lst.get(index++); + } + + @Override + public boolean hasMoreElements() { + return index < lst.size(); + } + + @Override + public void goFirst() { + index = 0; + } + + @Override + public int getLength() { + return lst.size(); + } +} diff --git a/NiHao/src/nihao/util/net/HttpClient.java b/NiHao/src/nihao/util/net/HttpClient.java new file mode 100644 index 0000000..7b81366 --- /dev/null +++ b/NiHao/src/nihao/util/net/HttpClient.java @@ -0,0 +1,76 @@ +package nihao.util.net; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import nihao.util.Conversor; + +public class HttpClient { + protected HttpURLConnection connection = null; + private String userAgent = "core.io.net.http"; + + public HttpClient(Url url) { + try { + if (url.getProxy() == null) + connection = (HttpURLConnection) url.getJavaUrl() + .openConnection(); + else { + Proxy proxy = url.getProxy(); + connection = (HttpURLConnection) url.getJavaUrl() + .openConnection(proxy.getJavaProxy()); + if (proxy.isAuth()) { + String encodedAuth = getBasicAuthBase64(proxy.getUser(), + proxy.getPass()); + setHeader("Proxy-Authorization", "Basic " + encodedAuth); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Pone una propiedad a la conexi�n + * + * @param property + * key + * @param value + * valor + */ + public void setHeader(String property, String value) { + connection.setRequestProperty(property, value); + } + + /** + * Ejecuta una conexi�n + * + * @param call + * objeto de llamada + */ + public void doCall(HttpClientCall call) { + + } + + // ------------------------------------------------- STATIC + /** + * Retorna el auth basico de usuario y contrase�a + * + * @param user + * @param pass + * @return + */ + public static String getBasicAuthBase64(String user, String pass) { + String password = user + ":" + pass; + return Conversor.bytesToBase64(Conversor.asciiToBytes(password)); + } + + // ------------------------------------------------- PROPERTIES + public String getUserAgent() { + return userAgent; + } + + public void setUserAgent(String userAgent) { + this.userAgent = userAgent; + } + +} diff --git a/NiHao/src/nihao/util/net/HttpClientCall.java b/NiHao/src/nihao/util/net/HttpClientCall.java new file mode 100644 index 0000000..d69517a --- /dev/null +++ b/NiHao/src/nihao/util/net/HttpClientCall.java @@ -0,0 +1,22 @@ +package nihao.util.net; + +public class HttpClientCall { + protected HttpRequest request; + protected HttpResponse response; + + public HttpClientCall() { + this.request = new HttpRequest(); + } + + public HttpClientCall(HttpRequest request) { + this.request = request; + } + + public HttpClientCall(NetMethod method, String url) { + request = new HttpRequest(method, url); + } + + public HttpClientCall(String method, String url) { + request = new HttpRequest(method, url); + } +} diff --git a/NiHao/src/nihao/util/net/HttpRequest.java b/NiHao/src/nihao/util/net/HttpRequest.java new file mode 100644 index 0000000..a6b9f9a --- /dev/null +++ b/NiHao/src/nihao/util/net/HttpRequest.java @@ -0,0 +1,29 @@ +package nihao.util.net; + +public class HttpRequest { + private String method; + private String url; + + public HttpRequest(){ + url="/"; + method=NetMethod.GET.name(); + } + + public HttpRequest(NetMethod method,String url){ + this.method=method.name(); + this.url=url; + } + + public HttpRequest(String method,String url){ + this.method=method; + this.url=url; + } + + public String getMethod() { + return method; + } + + public String getUrl() { + return url; + } +} diff --git a/NiHao/src/nihao/util/net/HttpResponse.java b/NiHao/src/nihao/util/net/HttpResponse.java new file mode 100644 index 0000000..7d93f52 --- /dev/null +++ b/NiHao/src/nihao/util/net/HttpResponse.java @@ -0,0 +1,5 @@ +package nihao.util.net; + +public class HttpResponse { + +} diff --git a/NiHao/src/nihao/util/net/NetConnection.java b/NiHao/src/nihao/util/net/NetConnection.java new file mode 100644 index 0000000..3d93e98 --- /dev/null +++ b/NiHao/src/nihao/util/net/NetConnection.java @@ -0,0 +1,201 @@ +package nihao.util.net; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URLEncoder; +import java.util.HashMap; + +import nihao.NiHaoException; + +public class NetConnection extends HttpClient { + private DataOutputStream output = null; + private boolean hasPost = false; + private boolean sended = false; + + /** + * Abre una conexi�n para la url indicada + * + * @param url + * Url a la que conectar + */ + public NetConnection(Url url, NetMethod method) { + super(url); + try { + connection.setRequestMethod(method.name()); + connection.setDoInput(true); + if (method == NetMethod.POST) { + connection.setDoOutput(true); + output = new DataOutputStream(connection.getOutputStream()); + } + connection.setUseCaches(false); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * A�ade un campo post + * + * @param name + * nombre del campo + * @param value + * valor del campo + */ + public void post(String name, String value) { + if (sended) + throw new NiHaoException("Can't post if sended"); + StringBuilder query = new StringBuilder(); + if (hasPost) + query.append('&'); + query.append(name); + query.append('='); + try { + query.append(URLEncoder.encode(value, "UTF-8")); + output.writeBytes(query.toString()); + } catch (IOException e) { + throw new RuntimeException(e); + } + hasPost = true; + } + + /** + * A�ade un conjunto de valores post desde un HashMap + * + * @param parameters + * HashMap con los vlaores + */ + public void post(HashMap parameters) { + for (String s : parameters.keySet()) { + post(s, parameters.get(s)); + } + } + + /** + * Envia un array de bytes al servidor + * + * @param data + * bytes a enviar + */ + public void write(byte[] data) { + try { + output.write(data); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Retorna el objeto de conexi�n + * + * @return HttpURLConnection + * @deprecated use HttpClient or extend + * NetConection + */ + public HttpURLConnection getConnectionObject() { + return connection; + } + + /** + * Termina el envio y retorna un InputStream con la respuesta + * del servidor + * + * @return InputStream con la respuesta del servidor + */ + public InputStream send() { + if (sended) + throw new NiHaoException("alreadey sended"); + try { + if (output != null) + output.close(); + sended = true; + return connection.getInputStream(); + } catch (IOException e) { + throw new QCoreNetException(getHttpCode(), e.getMessage(), connection.getErrorStream()); + } + } + + /** + * Establece un valor para la cabecera. + * + * @param key + * @param value + */ + public void setHeader(String key, String value) { + connection.setRequestProperty(key, value); + } + + /** + * Retorna el c�digo HTTP (100,404...) + * + * @return + */ + public int getHttpCode() { + if (!sended) + throw new NiHaoException("send first"); + try { + return connection.getResponseCode(); + } catch (IOException e1) { + return -1; + } + } + + /** + * Indica si ha de usar o no cache + * + * @param cache + */ + public void setCache(boolean cache) { + if (sended) + throw new NiHaoException("set cache before send"); + connection.setUseCaches(false); + } + + /** + * Retorna el Encoding del retorno + * + * @return String + */ + public String getContentEncoding() { + if (!sended) + throw new NiHaoException("send first"); + return connection.getContentEncoding(); + } + + /** + * Retorna el tipo mime (ContetType) del retorno + * + * @return String + */ + public String getContentType() { + if (!sended) + throw new NiHaoException("send first"); + return connection.getContentType(); + } + + /** + * Retorna el stream de error + * + * @return InputStream + */ + public InputStream getErrorStream() { + if (!sended) + throw new NiHaoException("send first"); + return connection.getErrorStream(); + } + + /** + * Retorna una linea del header de respuesta, la linea 0 es tratada de + * manera especial + * + * @param index + * indice de lines + * @return String + */ + public String getHeaderField(int index) { + if (!sended) + throw new NiHaoException("send first"); + return connection.getHeaderField(index); + } +} diff --git a/NiHao/src/nihao/util/net/NetMethod.java b/NiHao/src/nihao/util/net/NetMethod.java new file mode 100644 index 0000000..371bbd6 --- /dev/null +++ b/NiHao/src/nihao/util/net/NetMethod.java @@ -0,0 +1,22 @@ +package nihao.util.net; + +public enum NetMethod { + /** + * Standard Web HTTP get, generates a GET request + */ + GET, + + /** + * Standard Web HTTP post, generates a POST request + */ + POST, + /** + * Standard Web HTTP head, return only the header of a GET request + */ + HEAD, + + /** + * Standard Web HTTP options, return a list of the suppoted options + */ + OPTIONS +} diff --git a/NiHao/src/nihao/util/net/NetTools.java b/NiHao/src/nihao/util/net/NetTools.java new file mode 100644 index 0000000..7ac4608 --- /dev/null +++ b/NiHao/src/nihao/util/net/NetTools.java @@ -0,0 +1,66 @@ +package nihao.util.net; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.HashMap; + +import nihao.util.Conversor; +import nihao.util.IoUtil; + +/** + * Utilidades de red + * + * @author ivan.dominguez + * + */ +public class NetTools { + + /** + * Conecta con la url y retorna un array de bytes con la respuesta del servidor + * @param url Url con informaci�n de la conexi�n + * @return array de bytes con la respuesta del servidor + * @hint Memory consume option, for MemoryFriendly use httpGet + */ + public static byte[] httpGetBytes(Url url) { + InputStream result = httpGet(url); + if (result == null) + return null; + return IoUtil.inputStreamToByteArray(result); + } + + public static String readToEndString(InputStream result){ + if (result == null) + return null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int l; + byte[] tmp = new byte[2048]; + while ((l = result.read(tmp)) != -1) + baos.write(tmp, 0, l); + return Conversor.bytesToUTF8(baos.toByteArray()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Conecta con la url y retorna el InputStream con la respuesta del servidor + * @param url Url con informaci�n de la conexi�n + * @return InputStream con la respuesta del servidor + */ + public static InputStream httpGet(Url url) { + NetConnection con=url.getConnection(NetMethod.GET); + return con.send(); + } + + /** + * Conecta con la url por post y retorna el InputStream con la respuesta del servidor + * @param url Url con informaci�n de la conexi�n + * @return InputStream con la respuesta del servidor + */ + public static InputStream httpPost(Url url,HashMap parameters) { + NetConnection con=url.getConnection(NetMethod.POST); + con.post(parameters); + return con.send(); + } +} diff --git a/NiHao/src/nihao/util/net/Proxy.java b/NiHao/src/nihao/util/net/Proxy.java new file mode 100644 index 0000000..5023525 --- /dev/null +++ b/NiHao/src/nihao/util/net/Proxy.java @@ -0,0 +1,156 @@ +package nihao.util.net; + +import java.net.InetSocketAddress; + +/** + * Maneja informacion de un proxy + * + * @author ivan.dominguez + * + */ +public class Proxy { + private boolean isAuth = false; + private String user = null; + private String pass = null; + private String server = null; + private int port = 0; + + /** + * Genera un proxy vacio + */ + public Proxy() { + } + + /** + * Genera un proxy normal + * + * @param server + * Host del proxy + * @param port + * Puerto + */ + public Proxy(String server, Integer port) { + this.server = server; + this.port = port; + } + + /** + * Genera un proxy con autenticaci�n + * + * @param server + * Host del proxy + * @param port + * Puerto + * @param user + * Usuario + * @param pass + * Contrase�a + */ + public Proxy(String server, Integer port, String user, String pass) { + this.server = server; + this.port = port; + isAuth = true; + this.user = user; + this.pass = pass; + } + + /** + * Genera un java.net.Proxy con los datos actuales + * + * @return java.net.Proxy + */ + public java.net.Proxy getJavaProxy() { + return new java.net.Proxy(java.net.Proxy.Type.HTTP, new InetSocketAddress(server, port)); + } + + /** + * Indica si el proxy tiene autenticaci�n + * + * @return true si tiene autenticacion + */ + public boolean isAuth() { + return isAuth; + } + + /** + * Retorna el usuario de la autenticaci�n + * + * @return String con el nombre de usuario. + */ + public String getUser() { + return user; + } + + /** + * Establece el usuario de la autenticacion, esto activara el modo + * autenticado + * + * @param user + * String con el nombre de usuario. + */ + public void setUser(String user) { + this.user = user; + this.isAuth = true; + if (pass == null) + pass = ""; + } + + /** + * Retorna la contrase�a establecida para este proxy + * + * @return String con la contrase�a + */ + public String getPass() { + return pass; + } + + /** + * Establece la contrase�a de la autenticacion, esto activara el modo + * autenticado + * + * @param user + * String contrase�a + */ + public void setPass(String pass) { + this.pass = pass; + if (user == null) + user = ""; + } + + /** + * Retorna el host del proxy + * + * @return String host + */ + public String getServer() { + return server; + } + + /** + * Asigna el host del proxy + * + * @param server + * String host + */ + public void setServer(String server) { + this.server = server; + } + + /** + * Retorna el puerto del proxy + * + * @return + */ + public int getPort() { + return port; + } + + /** + * Asigna el puerto del proxy + * + * @param port + */ + public void setPort(int port) { + this.port = port; + } +} diff --git a/NiHao/src/nihao/util/net/QCoreNetException.java b/NiHao/src/nihao/util/net/QCoreNetException.java new file mode 100644 index 0000000..0d68abf --- /dev/null +++ b/NiHao/src/nihao/util/net/QCoreNetException.java @@ -0,0 +1,30 @@ +package nihao.util.net; + +import java.io.InputStream; + +import nihao.NiHaoException; + +public class QCoreNetException extends NiHaoException { + private static final long serialVersionUID = 1L; + + private int code; + private InputStream errstream; + + public QCoreNetException(int code, String msg, InputStream errstream) { + super(msg); + this.code = code; + this.errstream = errstream; + } + + public int getHttpCode() { + return code; + } + + public String getCodeDescription(){ + return null; + } + + public InputStream getErrorResponse() { + return errstream; + } +} diff --git a/NiHao/src/nihao/util/net/Url.java b/NiHao/src/nihao/util/net/Url.java new file mode 100644 index 0000000..ccdce6a --- /dev/null +++ b/NiHao/src/nihao/util/net/Url.java @@ -0,0 +1,122 @@ +package nihao.util.net; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Gestiona una Url + * + * @author ivan.dominguez + * + */ +public class Url { + /** + * Proxy por defecto, si no es null, cada new de Url asignara este proxy si + * este Proxy cambia NO afectara a las Urls ya instanciadas. + */ + public static Proxy DEFAULTPROXY = null; + + private String url; + private Proxy proxy; + + /** + * Crea un objeto vacio + */ + public Url() { + this.proxy = DEFAULTPROXY; + } + + /** + * Crea una Url a la ruta indicada, y con el proxy por defecto + * + * @param url + * Url del endpoint + */ + public Url(String url) { + this.url = url; + this.proxy = DEFAULTPROXY; + } + + /** + * Crea una Url con endpoint y proxy especificado + * + * @param url + * Endpoint + * @param proxy + * Informacion del proxy + */ + public Url(String url, Proxy proxy) { + this.url = url; + this.proxy = proxy; + } + + /** + * Genera una java.net.URL con la informacion actual + * + * @return Nuevo java.net.URL + */ + public URL getJavaUrl() { + try { + return new URL(url); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + /** + * Crea una conexi�n plenamente configurada + * + * @return NetConnection configurada y abierta la conexi�n + */ + public NetConnection getConnection(NetMethod method) { + return new NetConnection(this, method); + } + + /** + * Crea una conexi�n plenamente configurada + * + * @return HttpConnection + */ + public HttpClient getHttpClient() { + return new HttpClient(this); + } + + /** + * Url del endpoint + * + * @return + */ + public String getUrl() { + return url; + } + + /** + * Url del endpoint + * + * @param url + * String + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * Proxy de la conexi�n, o proxy por defecto en el momento del + * new + * + * @return + */ + public Proxy getProxy() { + return proxy; + } + + /** + * Establece el proxy para esta conexi�n + * + * @param proxy + * Proxy de la conexi�n + */ + public void setProxy(Proxy proxy) { + this.proxy = proxy; + } +} diff --git a/NiHao/src/nihao/util/reflection/Classes.java b/NiHao/src/nihao/util/reflection/Classes.java new file mode 100644 index 0000000..17fb86f --- /dev/null +++ b/NiHao/src/nihao/util/reflection/Classes.java @@ -0,0 +1,103 @@ +package nihao.util.reflection; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Vector; + +import javax.servlet.ServletContext; + +import nihao.NiHao; +import nihao.util.Resources; + +public class Classes { + private static String[] localClasses; + + public static void listLoadedClasses(ClassLoader byClassLoader) { + Class clKlass = byClassLoader.getClass(); + System.out.println("Classloader: " + clKlass.getCanonicalName()); + while (clKlass != java.lang.ClassLoader.class) { + clKlass = clKlass.getSuperclass(); + } + try { + java.lang.reflect.Field fldClasses = clKlass.getDeclaredField("classes"); + fldClasses.setAccessible(true); + Vector classes = (Vector) fldClasses.get(byClassLoader); + for (Iterator iter = classes.iterator(); iter.hasNext();) { + System.out.println(" Loaded " + iter.next()); + } + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + public static void listLoadedClasses() { + for (ClassLoader cl : Resources.getClassLoaders()) + listLoadedClasses(cl); + } + + /** + * Returns all the jars contained inside this WebApplication + * + * @return + */ + public static ArrayList listWebAppJars() { + ServletContext sctx = NiHao.getServletContext(); + ArrayList jars = new ArrayList(); + for (String jar : sctx.getResourcePaths("/WEB-INF/lib")) + jars.add(jar); + return jars; + } + + /** + * Returns a list of all classes inside the WebApp + */ + private static void readLocalDirectory(String path, ArrayList dest) { + ServletContext sctx = NiHao.getServletContext(); + for (String p : sctx.getResourcePaths(path)) { + if (p.charAt(p.length() - 1) == '/') + readLocalDirectory(p, dest); + else + dest.add(p); + } + } + + private static String[] listWebAppClasses() { + if (localClasses == null) { + ArrayList classes = new ArrayList(); + readLocalDirectory("/WEB-INF/classes", classes); + int i = 0; + while (i < classes.size()) { + String cls = classes.get(i); + if (cls.endsWith(".class")) { + cls = cls.substring(17, cls.length() - 6).replace('/', '.'); + classes.set(i, cls); + i++; + } else + classes.remove(i); + } + localClasses = classes.toArray(new String[classes.size()]); + } + return localClasses; + } + + public static ArrayList> getLocalClassesWithAnnotation(Class annotation) { + ArrayList> result = new ArrayList>(); + for (String clsPath : listWebAppClasses()) { + try { + Class c = Class.forName(clsPath); + if (c.getAnnotationsByType(annotation).length > 0) + result.add(c); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + return result; + } +} diff --git a/NiHao/src/nihao/util/reflection/ObjectType.java b/NiHao/src/nihao/util/reflection/ObjectType.java new file mode 100644 index 0000000..111645f --- /dev/null +++ b/NiHao/src/nihao/util/reflection/ObjectType.java @@ -0,0 +1,5 @@ +package nihao.util.reflection; + +public enum ObjectType { + Unknown, Primitive, String, ByteArray, Array, Object +} diff --git a/NiHao/src/nihao/util/reflection/Reflector.java b/NiHao/src/nihao/util/reflection/Reflector.java new file mode 100644 index 0000000..d46c6e2 --- /dev/null +++ b/NiHao/src/nihao/util/reflection/Reflector.java @@ -0,0 +1,471 @@ +package nihao.util.reflection; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import nihao.NiHaoException; +import nihao.util.Conversor; + +public class Reflector { + private static Map, Class> primitiveMap = new HashMap, Class>(); + private static final HashMap> CLASSALIAS = new HashMap>(); + static { + primitiveMap.put(boolean.class, Boolean.class); + primitiveMap.put(byte.class, Byte.class); + primitiveMap.put(char.class, Character.class); + primitiveMap.put(short.class, Short.class); + primitiveMap.put(int.class, Integer.class); + primitiveMap.put(long.class, Long.class); + primitiveMap.put(float.class, Float.class); + primitiveMap.put(double.class, Double.class); + CLASSALIAS.put("boolean", boolean.class); + CLASSALIAS.put("byte", byte.class); + CLASSALIAS.put("char", char.class); + CLASSALIAS.put("short", short.class); + CLASSALIAS.put("int", int.class); + CLASSALIAS.put("long", long.class); + CLASSALIAS.put("float", float.class); + CLASSALIAS.put("double", double.class); + CLASSALIAS.put("boolean", boolean.class); + CLASSALIAS.put("Byte", Byte.class); + CLASSALIAS.put("Character", Character.class); + CLASSALIAS.put("Short", Short.class); + CLASSALIAS.put("Integer", Integer.class); + CLASSALIAS.put("Long", Long.class); + CLASSALIAS.put("Float", Float.class); + CLASSALIAS.put("Double", Double.class); + CLASSALIAS.put("Boolean", Boolean.class); + CLASSALIAS.put("String", String.class); + CLASSALIAS.put("HashMap", HashMap.class); + CLASSALIAS.put("LiknedHashMap", LinkedHashMap.class); + } + + public static HashMap getHashMapFromObject(Object o) { + if (o == null) + return null; + try { + HashMap result = new HashMap(); + Class t = o.getClass(); + for (Method m : t.getMethods()) { + if ((!m.getName().startsWith("get") && !m.getName().startsWith("is")) || m.getParameterTypes().length > 0) + continue; + String key = m.getName(); + if (key.equals("getClass")) + continue; + if (key.startsWith("is")) + key = key.substring(2); + else + key = key.substring(3); + key = key.substring(0, 1).toLowerCase() + key.substring(1); + Object val = m.invoke(o); + result.put(key, val); + } + return result; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static ObjectType getObjectType(Object o) { + if (o == null) + return ObjectType.Unknown; + Class t = o.getClass(); + if (o instanceof String) + return ObjectType.String; + if (o instanceof byte[]) + return ObjectType.ByteArray; + if (t.isArray()) + return ObjectType.Array; + if (t.isPrimitive()) + return ObjectType.Primitive; + return ObjectType.Object; + } + + /** + * Coje la versi�n primitiva de una implementaci�n de primitiva, si la clase + * no es una implementaci�n de primitiva retorna null + * + * @param cls + * Class + * @return Class + */ + public static Class getPrimitiveClass(Class cls) { + for (Class primitive : primitiveMap.keySet()) + if (primitiveMap.get(primitive) == cls) + return primitive; + return null; + } + + /** + * Retorna la implementacion de primitiva de una primitiva, si la clase no + * es primitiva retorna null + * + * @param cls + * Class + * @return Class + */ + public static Class getPrimitiveImplementationClass(Class cls) { + if (primitiveMap.containsKey(cls)) + return primitiveMap.get(cls); + return null; + } + + /** + * Especifica si hay alg�n modo de conversion entre una clase y otra + * + * @param a + * Class + * @param b + * Class + * @return boolean + */ + public static boolean isCompatible(Class a, Class b) { + if (a == String.class) + return true; + if (a.isAssignableFrom(b)) + return true; + if (Reflector.getPrimitiveImplementationClass(a) == b) + return true; + if (Reflector.getPrimitiveImplementationClass(b) == a) + return true; + return false; + } + + /** + * Busca un metodo con tipos compatibles + * + * @param c + * Class + * @param methodName + * String + * @param paramTypes + * Class[] + * @return Method + */ + public static Method getCompatibleMethod(Class c, String methodName, Class... paramTypes) { + Method[] methods = c.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (!m.getName().equals(methodName)) + continue; + Class[] actualTypes = m.getParameterTypes(); + if (actualTypes.length != paramTypes.length) + continue; + boolean found = true; + for (int j = 0; j < actualTypes.length; j++) { + if (paramTypes[j] == null) + found = !actualTypes[j].isPrimitive(); + else if (!actualTypes[j].isAssignableFrom(paramTypes[j])) + if (actualTypes[j].isPrimitive()) + found = primitiveMap.get(actualTypes[j]).equals(paramTypes[j]); + else if (paramTypes[j].isPrimitive()) + found = primitiveMap.get(paramTypes[j]).equals(actualTypes[j]); + if (!found) + break; + } + if (found) + return m; + } + return null; + } + + /** + * Busca un constructor con tipos compatibles + * + * @param c + * Class + * @param paramTypes + * Class[] + * @return Constructor + */ + public static Constructor getCompatibleConstructor(Class c, Class... paramTypes) { + Constructor[] methods = c.getConstructors(); + for (int i = 0; i < methods.length; i++) { + Constructor m = methods[i]; + Class[] actualTypes = m.getParameterTypes(); + if (actualTypes.length != paramTypes.length) + continue; + boolean found = true; + for (int j = 0; j < actualTypes.length; j++) { + if (paramTypes[j] == null) + found = !actualTypes[j].isPrimitive(); + else if (!actualTypes[j].isAssignableFrom(paramTypes[j])) + if (actualTypes[j].isPrimitive()) + found = primitiveMap.get(actualTypes[j]).equals(paramTypes[j]); + else if (paramTypes[j].isPrimitive()) + found = primitiveMap.get(paramTypes[j]).equals(actualTypes[j]); + if (!found) + break; + } + if (found) + return m; + } + return null; + } + + /** + * Retorna el nombre de un getter a traves de el nombre de la propiedad + * + * @param fieldName + * String + * @return String true si el getter es de una propiedad booleana + */ + public static String getGetterName(String fieldName, boolean forBoolean) { + fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); + return (forBoolean ? "is" : "get") + fieldName; + } + + /** + * Retorna el nombre de un setter a traves de el nombre de la propiedad + * + * @param fieldName + * String + * @return String + */ + public static String getSetterName(String fieldName) { + fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); + return "set" + fieldName; + } + + /** + * Retorna el método getter de una clase para un nombre de propiedad, se + * asume el primer método con el nombre del getter + * + * @param cls + * Class, dónde buscar el getter + * @param name + * String, nombre de la propiedad a la que pertenece el setter + * @return Method + */ + public static Method getGetter(Class cls, String name) { + name = getGetterName(name, false); + for (Method m : cls.getMethods()) + if (m.getName().equals(name)) + return m; + name = getGetterName(name, true); + for (Method m : cls.getMethods()) + if (m.getName().equals(name)) + return m; + return null; + } + + /** + * Retorna el método setter de una clase, para un nombre de propiedad, se + * asume el primero con el nombre de la propiedad, ya que un setter solo + * tiene un tipo + * + * @param cls + * Class, dónde buscar el setter + * @param name + * String, nombre de la propiedad a la que pertenece el setter + * @return Method + */ + public static Method getSetter(Class cls, String name) { + name = getSetterName(name); + for (Method m : cls.getMethods()) + if (m.getName().equals(name) && m.getParameterCount() == 1) + return m; + return null; + } + + /** + * Retorna la clase desde un nombre dado, es como Class.forName, pero + * extendido + * + * @param name + * String + * @return Class + */ + public static Class getClass(String name) { + if (CLASSALIAS.containsKey(name)) + return CLASSALIAS.get(name); + try { + return Class.forName(name); + } catch (ClassNotFoundException e) { + throw new NiHaoException("Unknown class " + name, e); + } + } + + /** + * Retorna true si la clase es primitiva o se puede cambiar a primitiva + * + * @param cls + * @return + */ + public static boolean canBePrimitive(Class cls) { + if (cls.isPrimitive()) + return true; + for (Class p : primitiveMap.values()) + if (p == cls) + return true; + return false; + } + + // @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") + public static T compatibleCast(Object value, Class cls) { + if (value == null) + return null; + if (cls.isInstance(value)) + return cls.cast(value); + if (cls == String.class) + return (T) value.toString(); + Class vc = value.getClass(); + if (Number.class.isInstance(value)) { + Class c = Reflector.getPrimitiveImplementationClass(cls); + if (c == null) + c = cls; + Object result; + Number n = (Number) value; + if (c == Byte.class) + result = n.byteValue(); + else if (c == Short.class) + result = n.shortValue(); + else if (c == Integer.class) + result = n.intValue(); + else if (c == Long.class) + result = n.longValue(); + else if (c == Float.class) + result = n.floatValue(); + else if (c == Double.class) + result = n.doubleValue(); + else + throw new NiHaoException("Can't cast from " + vc.getName() + " to " + cls.getName()); + return (T) result; + } + if (Character.class.equals(value)) { + char ch = ((Character) value).charValue(); + if (cls == String.class) + return (T) ((Object) ("" + ch)); + throw new NiHaoException("Can't cast from " + vc.getName() + " to " + cls.getName()); + } + throw new NiHaoException("Can't cast from " + vc.getName() + " to " + cls.getName()); + } + + /** + * Retorna el tipo de array desde el tipo base + * + * @param componentClass + * @return + */ + public Class getArrayClass(Class componentClass) { + return Array.newInstance(componentClass, 0).getClass(); + } + + /** + * Coje el valor de un campo de un objecto, primero buscando su getter, y + * luego su field, si encuentra alguno retorna el valor + * + * @param + * Tipo de retorno + * @param o + * Objeto que contiene el campo + * @param fieldName + * String nombre del campo + * @param cls + * Clase del tipo de retorno + * @return T retorno + */ + public static T getFromObject(Object o, String fieldName, Class cls) { + Class ocls = o.getClass(); + Method m; + try { + m = getGetter(ocls, fieldName); + } catch (Throwable t) { + m = null; + } + if (m != null) { + try { + return Conversor.as(m.invoke(o), cls); + } catch (RuntimeException e) { + throw e; + } catch (Throwable t) { + throw new NiHaoException(t); + } + } + Field f; + try { + f = ocls.getField(fieldName); + if (f == null) + throw new NiHaoException("Reflector field or getter missing"); + return Conversor.as(f.get(o), cls); + } catch (RuntimeException e) { + throw e; + } catch (Throwable t) { + throw new NiHaoException(t); + } + } + + /** + * Navega a través de la ruta de un objeto y retorna el valor, usa los + * getters del objeto, o el campo interno.
+ * En caso de que algún campo sea null, la navegación se detiene y se + * retorna null + * + * @param + * tipo de retorno + * @param o + * Objeto por el que navegar + * @param path + * Ruta de navegación + * @param cls + * Clase de retorno + * @return Objeto del valor + */ + public static T getPathValue(Object o, String path, Class cls) { + if (path == null) + return null; + if (!path.contains(".")) + return getFromObject(o, path, cls); + String[] p = path.split("\\."); + return getPathValue(o, p, 0, cls); + } + + /** + * Navega a través de la ruta de un objeto y retorna el valor, usa los + * getters del objeto, o el campo interno.
+ * En caso de que algún campo sea null, la navegación se detiene y se + * retorna null + * + * @param + * tipo de retorno + * @param o + * Objeto por el que navegar + * @param path + * Ruta de navegación + * @param index + * Indice por dónde comenzar la navegación (usualmente 0) + * @param cls + * Clase de retorno + * @return Objeto del valor + */ + public static T getPathValue(Object o, String[] path, int index, Class cls) { + if (path == null) + return null; + for (int i = index; i < path.length; i++) { + o = getFromObject(o, path[i], Object.class); + if (o == null) + return null; + } + return Conversor.as(o, cls); + } + + /** + * Return true if the Method is a setter method + * + * @param m + * Method method + * @return boolean + */ + public static boolean isSetter(Method m) { + String name = m.getName(); + return name.startsWith("get") && name.length() > 3 && m.getParameterCount() == 1; + } +} \ No newline at end of file