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
+
+ 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
+
+
+ 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>
+
+ 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.
+
+ 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
+
+ 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
+
+ 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.
+
+ 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
+
+ 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
+
+
+ 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>
+
+ 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.
+
+ 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
+
+ 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
+
+ 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.
+
+ 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 extends Provider> provider) {
+ return get(provider, null);
+ }
+
+ public static ProviderInfo get(Class extends Provider> provider, Class extends Provider> 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