| @@ -0,0 +1,20 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <classpath> | |||
| <classpathentry kind="src" output="bin/main" path="src/main/java"> | |||
| <attributes> | |||
| <attribute name="gradle_scope" value="main"/> | |||
| <attribute name="gradle_used_by_scope" value="main,test"/> | |||
| </attributes> | |||
| </classpathentry> | |||
| <classpathentry kind="src" output="bin/test" path="src/test/java"> | |||
| <attributes> | |||
| <attribute name="gradle_scope" value="test"/> | |||
| <attribute name="gradle_used_by_scope" value="test"/> | |||
| <attribute name="test" value="true"/> | |||
| </attributes> | |||
| </classpathentry> | |||
| <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/> | |||
| <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/> | |||
| <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/> | |||
| <classpathentry kind="output" path="bin/default"/> | |||
| </classpath> | |||
| @@ -0,0 +1,2 @@ | |||
| #Wed May 25 11:46:18 CST 2022 | |||
| gradle.version=5.6.3 | |||
| @@ -0,0 +1,47 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <projectDescription> | |||
| <name>TBMS</name> | |||
| <comment></comment> | |||
| <projects> | |||
| </projects> | |||
| <buildSpec> | |||
| <buildCommand> | |||
| <name>org.eclipse.jdt.core.javabuilder</name> | |||
| <arguments> | |||
| </arguments> | |||
| </buildCommand> | |||
| <buildCommand> | |||
| <name>org.eclipse.wst.common.project.facet.core.builder</name> | |||
| <arguments> | |||
| </arguments> | |||
| </buildCommand> | |||
| <buildCommand> | |||
| <name>org.eclipse.wst.validation.validationbuilder</name> | |||
| <arguments> | |||
| </arguments> | |||
| </buildCommand> | |||
| <buildCommand> | |||
| <name>org.eclipse.buildship.core.gradleprojectbuilder</name> | |||
| <arguments> | |||
| </arguments> | |||
| </buildCommand> | |||
| </buildSpec> | |||
| <natures> | |||
| <nature>org.eclipse.jdt.core.javanature</nature> | |||
| <nature>org.eclipse.wst.common.project.facet.core.nature</nature> | |||
| <nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature> | |||
| <nature>org.eclipse.jem.workbench.JavaEMFNature</nature> | |||
| <nature>org.eclipse.buildship.core.gradleprojectnature</nature> | |||
| </natures> | |||
| <filteredResources> | |||
| <filter> | |||
| <id>0</id> | |||
| <name></name> | |||
| <type>30</type> | |||
| <matcher> | |||
| <id>org.eclipse.core.resources.regexFilterMatcher</id> | |||
| <arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments> | |||
| </matcher> | |||
| </filter> | |||
| </filteredResources> | |||
| </projectDescription> | |||
| @@ -0,0 +1,8 @@ | |||
| @echo off | |||
| copy /Y src\main\webapp\WEB-INF\classes\log4j.properties.prod src\main\webapp\WEB-INF\classes\log4j.properties | |||
| copy /Y src\main\webapp\WEB-INF\app.properties.local src\main\webapp\WEB-INF\app.properties | |||
| copy /Y src\main\webapp\META-INF\context.xml.2fi src\main\webapp\META-INF\context.xml | |||
| call a-clean-undeploy | |||
| call gradlew clean war | |||
| copy build\libs\%PROJECT_NAME%.war "%TC_BASE%\webapps" | |||
| call on | |||
| @@ -0,0 +1,8 @@ | |||
| @echo off | |||
| copy /Y src\main\webapp\WEB-INF\classes\log4j.properties.prod src\main\webapp\WEB-INF\classes\log4j.properties | |||
| copy /Y src\main\webapp\WEB-INF\app.properties.local src\main\webapp\WEB-INF\app.properties | |||
| copy /Y src\main\webapp\META-INF\context.xml.local src\main\webapp\META-INF\context.xml | |||
| call a-clean-undeploy | |||
| call gradlew clean war | |||
| copy build\libs\%PROJECT_NAME%.war "%TC_BASE%\webapps" | |||
| call on | |||
| @@ -0,0 +1,6 @@ | |||
| @echo off | |||
| call setenv | |||
| call off | |||
| del "%TC_BASE%\webapps\%PROJECT_NAME%.war" | |||
| rd /s /q "%TC_BASE%\webapps\%PROJECT_NAME%" | |||
| rd /s /q "%TC_BASE%\work\Catalina\localhost\%PROJECT_NAME%" | |||
| @@ -0,0 +1,5 @@ | |||
| @echo off | |||
| copy /Y src\main\webapp\WEB-INF\classes\log4j.properties.prod src\main\webapp\WEB-INF\classes\log4j.properties | |||
| copy /Y src\main\webapp\WEB-INF\app.properties.prod src\main\webapp\WEB-INF\app.properties | |||
| copy /Y src\main\webapp\META-INF\context.xml.2fi src\main\webapp\META-INF\context.xml | |||
| call gradlew clean war | |||
| @@ -0,0 +1,89 @@ | |||
| apply plugin: 'java' | |||
| apply plugin: 'eclipse' | |||
| apply plugin: 'war' | |||
| sourceCompatibility = 11 | |||
| targetCompatibility = 11 | |||
| tasks.withType(JavaCompile) { | |||
| options.encoding = 'UTF-8' | |||
| } | |||
| repositories { | |||
| jcenter() | |||
| mavenCentral() | |||
| maven { | |||
| url 'http://jaspersoft.jfrog.io/jaspersoft/third-party-ce-artifacts/' | |||
| } | |||
| } | |||
| war { | |||
| archiveName = 'tbms.war' | |||
| classpath = classpath - sourceSets.main.output | |||
| from (jar) { | |||
| into 'WEB-INF/lib' | |||
| } | |||
| } | |||
| dependencies { | |||
| // for JDK 11 | |||
| compile 'javax.xml.bind:jaxb-api:2.3.1' | |||
| compile 'javax.annotation:javax.annotation-api:1.3.2' | |||
| compile 'com.sun.activation:javax.activation:1.2.0' | |||
| compile 'org.springframework:spring-context:4.3.14.RELEASE' | |||
| compile 'org.springframework:spring-context-support:4.3.14.RELEASE' | |||
| compile 'org.springframework:spring-orm:4.3.14.RELEASE' | |||
| compile 'org.springframework:spring-webmvc:4.3.14.RELEASE' | |||
| compile 'org.springframework.security:spring-security-config:4.2.3.RELEASE' | |||
| compile 'org.springframework.security:spring-security-web:4.2.3.RELEASE' | |||
| compile 'org.apache.commons:commons-lang3:3.5' | |||
| compile 'commons-fileupload:commons-fileupload:1.3.2' | |||
| compile 'org.apache.poi:poi:3.17' | |||
| compile 'org.apache.poi:poi-ooxml:3.17' | |||
| compile 'org.freemarker:freemarker:2.3.23' | |||
| compile 'org.hibernate:hibernate-core:5.2.12.Final' | |||
| compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final' | |||
| compile 'com.fasterxml.jackson.core:jackson-core:2.8.8' | |||
| compile 'com.fasterxml.jackson.core:jackson-annotations:2.8.8' | |||
| compile 'com.fasterxml.jackson.core:jackson-databind:2.8.8.1' | |||
| compile 'org.imgscalr:imgscalr-lib:4.2' | |||
| compile(group: 'net.sf.jasperreports', name: 'jasperreports', version:'6.5.1') { | |||
| exclude(module: 'bcmail-jdk14') | |||
| exclude(module: 'bcprov-jdk14') | |||
| exclude(module: 'bctsp-jdk14') | |||
| exclude(module: 'castor-xml') | |||
| exclude(module: 'jackson-core') | |||
| exclude(module: 'jackson-annotations') | |||
| exclude(module: 'jackson-databind') | |||
| exclude(module: 'lucene-core') | |||
| exclude(module: 'lucene-analyzers-common') | |||
| exclude(module: 'lucene-queryparser') | |||
| exclude(module: 'olap4j') | |||
| } | |||
| //providedCompile 'org.slf4j:slf4j-api:1.7.25' | |||
| //providedCompile 'org.slf4j:slf4j-log4j12:1.7.25' | |||
| providedCompile 'javax.servlet:javax.servlet-api:3.1.0' | |||
| providedCompile 'mysql:mysql-connector-java:8.0.15' | |||
| //testCompile 'junit:junit:4.12' | |||
| compile 'com.google.zxing:javase:3.2.1' | |||
| compile 'javax.mail:mail:1.4.7' | |||
| compile group: 'org.liquibase', name: 'liquibase-core', version: '3.5.3' | |||
| fileTree(dir: 'lib', include: '*.jar') | |||
| } | |||
| @@ -0,0 +1,295 @@ | |||
| -- anna 20/03/2020 | |||
| -- | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('MEETING_MAINTAIN', 'Meeting', 'Maintain Meeting'); | |||
| INSERT INTO `tbmsdb`.`users_authorities` (`userId`, `authority`) VALUES ('1', 'MEETING_MAINTAIN'); | |||
| ALTER TABLE `tbmsdb`.`meeting` CHANGE COLUMN `datetime` `date` DATE NOT NULL ; | |||
| -- anna 23/03/2020 | |||
| -- | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('ORDER_MAINTAIN', 'order', 'Maintain order'); | |||
| UPDATE `tbmsdb`.`authorities` SET `module`='customer' WHERE `authority`='CUSTOMER_MAINTAIN'; | |||
| UPDATE `tbmsdb`.`authorities` SET `module`='meeting' WHERE `authority`='MEETING_MAINTAIN'; | |||
| INSERT INTO `tbmsdb`.`users_authorities` (`userId`, `authority`) VALUES ('1', 'ORDER_MAINTAIN'); | |||
| ALTER TABLE `tbmsdb`.`order` RENAME TO `tbmsdb`.`orders` ; | |||
| -- anna 31/03/2020 | |||
| -- | |||
| ALTER TABLE `tbmsdb`.`orders` | |||
| ADD COLUMN `refNo` VARCHAR(100) NULL DEFAULT NULL AFTER `ref`; | |||
| -- anna 07/04/2020 | |||
| -- | |||
| ALTER TABLE `tbmsdb`.`files` | |||
| ADD COLUMN `skey` VARCHAR(16) NOT NULL AFTER `description`; | |||
| -- anna 09/04/2020 | |||
| -- | |||
| ALTER TABLE `tbmsdb`.`customer` | |||
| ADD COLUMN `companyName` VARCHAR(255) NULL AFTER `phone2`; | |||
| -- anna 14/04/2020 | |||
| -- | |||
| ALTER TABLE `tbmsdb`.`customer` | |||
| CHANGE COLUMN `surname` `surname` VARCHAR(255) NULL DEFAULT NULL , | |||
| CHANGE COLUMN `firstName` `firstName` VARCHAR(255) NULL DEFAULT NULL ; | |||
| -- anna 11/05/2020 | |||
| -- | |||
| ALTER TABLE `tbmsdb`.`orders` | |||
| ADD INDEX `cust_order` (`id` ASC, `custId` ASC); | |||
| ALTER TABLE `tbmsdb`.`orders` | |||
| ADD INDEX `order_ref` (`id` ASC, `ref` ASC); | |||
| ALTER TABLE `tbmsdb`.`customer` | |||
| ADD INDEX `cust_ref` (`id` ASC, `ref` ASC); | |||
| -- anna 15/05/2020 | |||
| -- | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`, `description`) VALUES ('AMOUNT_REMIND', 'order', 'Order Amount Remind', NULL); | |||
| DROP TABLE IF EXISTS `audit_log`; | |||
| CREATE TABLE `audit_log` ( | |||
| `tableName` varchar(30) NOT NULL, | |||
| `recordId` int(11) NOT NULL, | |||
| `modifiedBy` int(11) DEFAULT NULL, | |||
| `modified` datetime DEFAULT NULL, | |||
| `oldData` json DEFAULT NULL, | |||
| `newData` json DEFAULT NULL | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |||
| -- anna 28/05/2020 | |||
| ALTER TABLE `tbmsdb`.`meeting` | |||
| CHANGE COLUMN `date` `date` DATETIME NOT NULL ; | |||
| -- Matthew | |||
| -- 09/06/2020 | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('ORDER_CANCEL', 'order', 'Order Cancel'); | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('MEETING_DELETE', 'meeting', 'Meeting Delete'); | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('CUSTOMER_DELETE', 'customer', 'Customer Delete'); | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('FILE_DELETE', 'file', 'File Delete'); | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('PAYMENT_REPORT', 'report', 'Payment Report Auth'); | |||
| -- Jason Lam | |||
| -- 10/06/2020 | |||
| ALTER TABLE `tbmsdb`.`orders` | |||
| ADD COLUMN `outDate` DATE NULL AFTER `status`, | |||
| ADD COLUMN `outRemark` VARCHAR(255) NULL AFTER `outDate`, | |||
| ADD COLUMN `inDate` DATE NULL AFTER `outRemark`, | |||
| ADD COLUMN `inRemark` VARCHAR(255) NULL AFTER `inDate`; | |||
| --Jason Lam | |||
| --11/06/2020 | |||
| ALTER TABLE `tbmsdb`.`orders` | |||
| ADD COLUMN `ignore` TINYINT(1) '0' AFTER `inRemark`; | |||
| ALTER TABLE `tbmsdb`.`orders` | |||
| CHANGE COLUMN `ignore` `ignoreC` TINYINT(1) NULL DEFAULT '0' ; | |||
| -- anna | |||
| -- 15/06/2020 | |||
| ALTER TABLE `tbmsdb`.`audit_log` | |||
| DROP COLUMN `oldData`, | |||
| CHANGE COLUMN `newData` `dataStr` JSON NULL DEFAULT NULL , | |||
| ADD COLUMN `actionStr` VARCHAR(255) NULL DEFAULT NULL AFTER `modified`, | |||
| ADD COLUMN `refId` INT(11) NULL DEFAULT NULL AFTER `dataStr`, | |||
| ADD COLUMN `refType` VARCHAR(255) NULL DEFAULT NULL AFTER `refId`; | |||
| -- anna | |||
| -- 23/06/2020 | |||
| ALTER TABLE `tbmsdb`.`orders` | |||
| ADD COLUMN `workshopStatus` INT(11) NULL DEFAULT NULL AFTER `ignoreC`; | |||
| -- anna | |||
| -- 29/06/2020 | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('ORDER_ITEM_DELETE', 'order', 'Order Item Delete'); | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('PAYMENT_DELETE', 'order', 'Payment Delete'); | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('WORKSHOP_MAINTAIN', 'workshop', 'Maintain Workshop'); | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('CALENDAR_VIEW', 'calendar', 'View Calendar'); | |||
| -- anna | |||
| -- 13/07/2020 | |||
| ALTER TABLE `tbmsdb`.`orders` | |||
| ADD COLUMN `targetCompleteDate` DATE NULL DEFAULT NULL AFTER `workshopStatus`; | |||
| -- anna | |||
| -- 21/07/2020 | |||
| ALTER TABLE `tbmsdb`.`customer` | |||
| ADD COLUMN `tag` VARCHAR(255) NULL DEFAULT NULL AFTER `ref`; | |||
| -- matthew | |||
| -- 07/09/2020 | |||
| INSERT INTO `tbmsdb`.`settings` (`name`, `value`) VALUES ('JS.version', '1'); | |||
| -- anna | |||
| -- 01/12/2020 | |||
| ALTER TABLE `tbmsdb`.`meeting` | |||
| ADD COLUMN `staffName` VARCHAR(45) NULL DEFAULT NULL AFTER `remarks`; | |||
| ALTER TABLE `tbmsdb`.`orders` | |||
| ADD COLUMN `inStatus` INT(11) NULL DEFAULT NULL AFTER `targetCompleteDate`, | |||
| ADD COLUMN `outStatus` INT(11) NULL DEFAULT NULL AFTER `inStatus`; | |||
| -- anna | |||
| -- 10/06/2021 | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('MATERIAL_MAINTAIN', 'price_list', 'Maintain Material'); | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('BAND_DELETE', 'price_list', 'Delete Band'); | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('VIEW_EXPECTED_PRICE', 'price_list', 'View Expected Price'); | |||
| -- anna | |||
| -- 29/06/2021 | |||
| INSERT INTO `tbmsdb`.`settings` (`name`, `value`) VALUES ('MAIL.temp.subject', '[INVOICE] Order No.: {$orderNo}'); | |||
| -- anna | |||
| -- 06/07/2021 | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('MATERIAL_MAIL_TEMPLATE', 'setup', 'Maintain Mail Template'); | |||
| INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('PRICE_LIST_VIEW', 'price_list', 'View Price List'); | |||
| UPDATE `tbmsdb`.`authorities` SET `authority`='PRICE_LIST_MAINTAIN', `name`='Maintain Price List' WHERE `authority`='MATERIAL_MAINTAIN'; | |||
| -- anna | |||
| -- 19/07/2021 | |||
| CREATE TABLE `material_band` ( | |||
| `id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `versionId` mediumint(8) unsigned NOT NULL DEFAULT '0', | |||
| `deleted` tinyint(1) NOT NULL DEFAULT '0', | |||
| `createdBy` int(11) DEFAULT NULL, | |||
| `modifiedBy` int(11) DEFAULT NULL, | |||
| `created` datetime DEFAULT NULL, | |||
| `modified` datetime DEFAULT NULL, | |||
| `name` varchar(255) NOT NULL, | |||
| `fileId` int(11) DEFAULT NULL, | |||
| `expectedPriceFormula` varchar(255) DEFAULT NULL, | |||
| PRIMARY KEY (`id`) | |||
| ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; | |||
| CREATE TABLE `material_item` ( | |||
| `id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `versionId` mediumint(8) unsigned NOT NULL DEFAULT '0', | |||
| `deleted` tinyint(1) NOT NULL DEFAULT '0', | |||
| `createdBy` int(11) DEFAULT NULL, | |||
| `modifiedBy` int(11) DEFAULT NULL, | |||
| `created` datetime DEFAULT NULL, | |||
| `modified` datetime DEFAULT NULL, | |||
| `bandId` int(11) DEFAULT NULL, | |||
| `name` varchar(255) DEFAULT NULL, | |||
| `bunch` varchar(255) DEFAULT NULL, | |||
| `itemNo` varchar(255) DEFAULT NULL, | |||
| `cost` decimal(18,4) DEFAULT '0.0000', | |||
| `suitPrice` decimal(18,4) DEFAULT NULL, | |||
| `jacketPrice` decimal(18,4) DEFAULT NULL, | |||
| `overcoatPrice` decimal(18,4) DEFAULT NULL, | |||
| `pantsPrice` decimal(18,4) DEFAULT NULL, | |||
| `otherPrice` decimal(18,4) DEFAULT NULL, | |||
| PRIMARY KEY (`id`) | |||
| ) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8; | |||
| -- anna | |||
| -- 10/01/2022 | |||
| ALTER TABLE `tbmsdb`.`material_item` | |||
| ADD COLUMN `fabricNum` VARCHAR(255) NULL DEFAULT NULL AFTER `bunch`; | |||
| -- anna | |||
| -- 4/03/2022 | |||
| ALTER TABLE `tbmsdb`.`material_item` | |||
| ADD COLUMN `orderIdx` INT(11) NOT NULL DEFAULT 0 AFTER `otherPrice`; | |||
| CREATE TABLE `user_settings` ( | |||
| `userId` int(11) NOT NULL, | |||
| `name` varchar(255) NOT NULL, | |||
| `value` varchar(1000) DEFAULT NULL, | |||
| `category` varchar(50) DEFAULT NULL, | |||
| `type` varchar(45) DEFAULT NULL, | |||
| PRIMARY KEY (`userId`,`name`) | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |||
| -- anna | |||
| -- 22/03/2022 | |||
| CREATE TABLE `sys_groups` ( | |||
| `id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `deleted` tinyint(1) NOT NULL, | |||
| `versionId` mediumint(8) unsigned NOT NULL, | |||
| `created` datetime DEFAULT NULL, | |||
| `createdBy` int(11) DEFAULT NULL, | |||
| `modified` datetime DEFAULT NULL, | |||
| `modifiedBy` int(11) DEFAULT NULL, | |||
| `name` varchar(50) NOT NULL, | |||
| `description` varchar(255) DEFAULT NULL, | |||
| PRIMARY KEY (`id`) | |||
| ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; | |||
| ALTER TABLE `tbmsdb`.`groups` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`users` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`customer` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`files` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`files_blob` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`files_ref` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`material_band` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`material_item` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`meeting` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`order_item` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`orders` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`payment` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| ALTER TABLE `tbmsdb`.`message` | |||
| ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||
| -- anna | |||
| -- 18/05/2022 | |||
| ALTER TABLE `tbmsdb`.`sys_groups` | |||
| ADD COLUMN `mailBody` TEXT NULL DEFAULT NULL AFTER `description`, | |||
| ADD COLUMN `mailSubject` TEXT NULL DEFAULT NULL AFTER `mailBody`; | |||
| ALTER TABLE `tbmsdb`.`sys_groups` | |||
| ADD COLUMN `companyName` VARCHAR(255) NULL DEFAULT NULL AFTER `mailSubject`, | |||
| ADD COLUMN `phone` VARCHAR(255) NULL DEFAULT NULL AFTER `companyName`, | |||
| ADD COLUMN `fax` VARCHAR(255) NULL DEFAULT NULL AFTER `phone`, | |||
| ADD COLUMN `email` VARCHAR(255) NULL DEFAULT NULL AFTER `fax`, | |||
| ADD COLUMN `address` VARCHAR(500) NULL DEFAULT NULL AFTER `email`; | |||
| -- anna | |||
| -- 23/05/2022 | |||
| ALTER TABLE `tbmsdb`.`sys_groups` | |||
| ADD COLUMN `smtp_host` VARCHAR(255) NULL DEFAULT NULL AFTER `address`, | |||
| ADD COLUMN `smtp_port` VARCHAR(5) NULL DEFAULT NULL AFTER `smtp_host`, | |||
| ADD COLUMN `smtp_username` VARCHAR(255) NULL DEFAULT NULL AFTER `smtp_port`, | |||
| ADD COLUMN `smtp_password` VARCHAR(255) NULL DEFAULT NULL AFTER `smtp_username`; | |||
| @@ -0,0 +1,12 @@ | |||
| -- | |||
| -- ON stock_ledger INSERT: SET part.balance = stock_ledger.balance | |||
| -- | |||
| DROP TRIGGER IF EXISTS `tbmsdb`.`stock_ledger_AFTER_INSERT`; | |||
| DELIMITER $$ | |||
| USE `tbmsdb`$$ | |||
| CREATE DEFINER = `root`@`localhost` TRIGGER `tbmsdb`.`stock_ledger_AFTER_INSERT` AFTER INSERT ON `stock_ledger` FOR EACH ROW | |||
| BEGIN | |||
| UPDATE `part` SET `balance` = NEW.`balance` WHERE `id` = NEW.`partId`; | |||
| END$$ | |||
| DELIMITER ; | |||
| @@ -0,0 +1,6 @@ | |||
| -- SET FOREIGN_KEY_CHECKS=0; | |||
| -- TRUNCATE `job`; | |||
| -- SET FOREIGN_KEY_CHECKS=1; | |||
| @@ -0,0 +1,3 @@ | |||
| #!/bin/bash | |||
| source ./setenv.sh | |||
| cp -r src/main/webapp/resources/js/* ${tc_instance}/webapps/sms/resources/js/ | |||
| @@ -0,0 +1,3 @@ | |||
| @ECHO OFF | |||
| rd /S /Q C:\Tomcat\webapps\tbms\resources | |||
| mklink /J C:\Tomcat\webapps\tbms\resources C:\workspace\TBMS\src\main\webapp\resources | |||
| @@ -0,0 +1,4 @@ | |||
| #!/bin/bash | |||
| bash ./undeploy.sh | |||
| bash ./publish.sh | |||
| bash ./on.sh | |||
| @@ -0,0 +1,47 @@ | |||
| Manage Master Data | |||
| Vendor | |||
| Equipment Type | |||
| Equipment | |||
| Equipment Part | |||
| Equipment and Parts relation | |||
| Manage Preventive Maintenance (PM) Plan | |||
| Setup Preventive Maintenance Plan | |||
| Equipment | |||
| Plan name and description (e.g. checklist) | |||
| Frequency (e.g. Daily, Weekly, Monthly, Yearly) | |||
| Resp. Team/Person | |||
| Preferred timeslot (or Shift) | |||
| Preferred day of week (e.g. Monday, Tuesday) | |||
| Time required to perform the PM | |||
| Support file attachments | |||
| Provide daily and upcoming pending PM plan (i.e. Pending to do list) | |||
| Display all the details above, plus Last Performed Date | |||
| Upon PM completion, maintenance staff will create Service Log to record, then PM plan is completed (by matching Service Log with corresponding PM plan) | |||
| Incident Log should be created if any issues found during PM | |||
| Manage Incident | |||
| Search Incident Logs | |||
| Create/Edit Incident Logs | |||
| Support file attachments | |||
| If part replacement is required, it's used as the base for Part Stock Out | |||
| Manage Problem | |||
| Search Problem Logs | |||
| Create/Edit Problem Logs | |||
| Support linking to related Incident Logs | |||
| Support file attachments | |||
| Manage Parts Inventory | |||
| Stock In/Out and Adjustment | |||
| Search Parts | |||
| Create/Edit Parts | |||
| Support re-order level, can filter list of parts that the balance is below re-order level | |||
| Support First-In-First-Out for Stock Out ??? | |||
| Parts I/O Ledger | |||
| Android App | |||
| View PM Plans | |||
| Input Service and Incident Logs | |||
| Receive Incident service requests | |||
| @@ -0,0 +1,5 @@ | |||
| distributionBase=GRADLE_USER_HOME | |||
| distributionPath=wrapper/dists | |||
| distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip | |||
| zipStoreBase=GRADLE_USER_HOME | |||
| zipStorePath=wrapper/dists | |||
| @@ -0,0 +1,188 @@ | |||
| #!/usr/bin/env sh | |||
| # | |||
| # Copyright 2015 the original author or authors. | |||
| # | |||
| # Licensed under the Apache License, Version 2.0 (the "License"); | |||
| # you may not use this file except in compliance with the License. | |||
| # You may obtain a copy of the License at | |||
| # | |||
| # http://www.apache.org/licenses/LICENSE-2.0 | |||
| # | |||
| # Unless required by applicable law or agreed to in writing, software | |||
| # distributed under the License is distributed on an "AS IS" BASIS, | |||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| # See the License for the specific language governing permissions and | |||
| # limitations under the License. | |||
| # | |||
| ############################################################################## | |||
| ## | |||
| ## Gradle start up script for UN*X | |||
| ## | |||
| ############################################################################## | |||
| # Attempt to set APP_HOME | |||
| # Resolve links: $0 may be a link | |||
| PRG="$0" | |||
| # Need this for relative symlinks. | |||
| while [ -h "$PRG" ] ; do | |||
| ls=`ls -ld "$PRG"` | |||
| link=`expr "$ls" : '.*-> \(.*\)$'` | |||
| if expr "$link" : '/.*' > /dev/null; then | |||
| PRG="$link" | |||
| else | |||
| PRG=`dirname "$PRG"`"/$link" | |||
| fi | |||
| done | |||
| SAVED="`pwd`" | |||
| cd "`dirname \"$PRG\"`/" >/dev/null | |||
| APP_HOME="`pwd -P`" | |||
| cd "$SAVED" >/dev/null | |||
| APP_NAME="Gradle" | |||
| APP_BASE_NAME=`basename "$0"` | |||
| # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | |||
| DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | |||
| # Use the maximum available, or set MAX_FD != -1 to use that value. | |||
| MAX_FD="maximum" | |||
| warn () { | |||
| echo "$*" | |||
| } | |||
| die () { | |||
| echo | |||
| echo "$*" | |||
| echo | |||
| exit 1 | |||
| } | |||
| # OS specific support (must be 'true' or 'false'). | |||
| cygwin=false | |||
| msys=false | |||
| darwin=false | |||
| nonstop=false | |||
| case "`uname`" in | |||
| CYGWIN* ) | |||
| cygwin=true | |||
| ;; | |||
| Darwin* ) | |||
| darwin=true | |||
| ;; | |||
| MINGW* ) | |||
| msys=true | |||
| ;; | |||
| NONSTOP* ) | |||
| nonstop=true | |||
| ;; | |||
| esac | |||
| CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar | |||
| # Determine the Java command to use to start the JVM. | |||
| if [ -n "$JAVA_HOME" ] ; then | |||
| if [ -x "$JAVA_HOME/jre/sh/java" ] ; then | |||
| # IBM's JDK on AIX uses strange locations for the executables | |||
| JAVACMD="$JAVA_HOME/jre/sh/java" | |||
| else | |||
| JAVACMD="$JAVA_HOME/bin/java" | |||
| fi | |||
| if [ ! -x "$JAVACMD" ] ; then | |||
| die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME | |||
| Please set the JAVA_HOME variable in your environment to match the | |||
| location of your Java installation." | |||
| fi | |||
| else | |||
| JAVACMD="java" | |||
| which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | |||
| Please set the JAVA_HOME variable in your environment to match the | |||
| location of your Java installation." | |||
| fi | |||
| # Increase the maximum file descriptors if we can. | |||
| if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then | |||
| MAX_FD_LIMIT=`ulimit -H -n` | |||
| if [ $? -eq 0 ] ; then | |||
| if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then | |||
| MAX_FD="$MAX_FD_LIMIT" | |||
| fi | |||
| ulimit -n $MAX_FD | |||
| if [ $? -ne 0 ] ; then | |||
| warn "Could not set maximum file descriptor limit: $MAX_FD" | |||
| fi | |||
| else | |||
| warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" | |||
| fi | |||
| fi | |||
| # For Darwin, add options to specify how the application appears in the dock | |||
| if $darwin; then | |||
| GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" | |||
| fi | |||
| # For Cygwin, switch paths to Windows format before running java | |||
| if $cygwin ; then | |||
| APP_HOME=`cygpath --path --mixed "$APP_HOME"` | |||
| CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` | |||
| JAVACMD=`cygpath --unix "$JAVACMD"` | |||
| # We build the pattern for arguments to be converted via cygpath | |||
| ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` | |||
| SEP="" | |||
| for dir in $ROOTDIRSRAW ; do | |||
| ROOTDIRS="$ROOTDIRS$SEP$dir" | |||
| SEP="|" | |||
| done | |||
| OURCYGPATTERN="(^($ROOTDIRS))" | |||
| # Add a user-defined pattern to the cygpath arguments | |||
| if [ "$GRADLE_CYGPATTERN" != "" ] ; then | |||
| OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" | |||
| fi | |||
| # Now convert the arguments - kludge to limit ourselves to /bin/sh | |||
| i=0 | |||
| for arg in "$@" ; do | |||
| CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` | |||
| CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option | |||
| if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition | |||
| eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` | |||
| else | |||
| eval `echo args$i`="\"$arg\"" | |||
| fi | |||
| i=$((i+1)) | |||
| done | |||
| case $i in | |||
| (0) set -- ;; | |||
| (1) set -- "$args0" ;; | |||
| (2) set -- "$args0" "$args1" ;; | |||
| (3) set -- "$args0" "$args1" "$args2" ;; | |||
| (4) set -- "$args0" "$args1" "$args2" "$args3" ;; | |||
| (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; | |||
| (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; | |||
| (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; | |||
| (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; | |||
| (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; | |||
| esac | |||
| fi | |||
| # Escape application args | |||
| save () { | |||
| for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done | |||
| echo " " | |||
| } | |||
| APP_ARGS=$(save "$@") | |||
| # Collect all arguments for the java command, following the shell quoting and substitution rules | |||
| eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" | |||
| # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong | |||
| if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then | |||
| cd "$(dirname "$0")" | |||
| fi | |||
| exec "$JAVACMD" "$@" | |||
| @@ -0,0 +1,100 @@ | |||
| @rem | |||
| @rem Copyright 2015 the original author or authors. | |||
| @rem | |||
| @rem Licensed under the Apache License, Version 2.0 (the "License"); | |||
| @rem you may not use this file except in compliance with the License. | |||
| @rem You may obtain a copy of the License at | |||
| @rem | |||
| @rem http://www.apache.org/licenses/LICENSE-2.0 | |||
| @rem | |||
| @rem Unless required by applicable law or agreed to in writing, software | |||
| @rem distributed under the License is distributed on an "AS IS" BASIS, | |||
| @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| @rem See the License for the specific language governing permissions and | |||
| @rem limitations under the License. | |||
| @rem | |||
| @if "%DEBUG%" == "" @echo off | |||
| @rem ########################################################################## | |||
| @rem | |||
| @rem Gradle startup script for Windows | |||
| @rem | |||
| @rem ########################################################################## | |||
| @rem Set local scope for the variables with windows NT shell | |||
| if "%OS%"=="Windows_NT" setlocal | |||
| set DIRNAME=%~dp0 | |||
| if "%DIRNAME%" == "" set DIRNAME=. | |||
| set APP_BASE_NAME=%~n0 | |||
| set APP_HOME=%DIRNAME% | |||
| @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | |||
| set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" | |||
| @rem Find java.exe | |||
| if defined JAVA_HOME goto findJavaFromJavaHome | |||
| set JAVA_EXE=java.exe | |||
| %JAVA_EXE% -version >NUL 2>&1 | |||
| if "%ERRORLEVEL%" == "0" goto init | |||
| echo. | |||
| echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | |||
| echo. | |||
| echo Please set the JAVA_HOME variable in your environment to match the | |||
| echo location of your Java installation. | |||
| goto fail | |||
| :findJavaFromJavaHome | |||
| set JAVA_HOME=%JAVA_HOME:"=% | |||
| set JAVA_EXE=%JAVA_HOME%/bin/java.exe | |||
| if exist "%JAVA_EXE%" goto init | |||
| echo. | |||
| echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% | |||
| echo. | |||
| echo Please set the JAVA_HOME variable in your environment to match the | |||
| echo location of your Java installation. | |||
| goto fail | |||
| :init | |||
| @rem Get command-line arguments, handling Windows variants | |||
| if not "%OS%" == "Windows_NT" goto win9xME_args | |||
| :win9xME_args | |||
| @rem Slurp the command line arguments. | |||
| set CMD_LINE_ARGS= | |||
| set _SKIP=2 | |||
| :win9xME_args_slurp | |||
| if "x%~1" == "x" goto execute | |||
| set CMD_LINE_ARGS=%* | |||
| :execute | |||
| @rem Setup the command line | |||
| set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar | |||
| @rem Execute Gradle | |||
| "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% | |||
| :end | |||
| @rem End local scope for the variables with windows NT shell | |||
| if "%ERRORLEVEL%"=="0" goto mainEnd | |||
| :fail | |||
| rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of | |||
| rem the _cmd.exe /c_ return code! | |||
| if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 | |||
| exit /b 1 | |||
| :mainEnd | |||
| if "%OS%"=="Windows_NT" endlocal | |||
| :omega | |||
| @@ -0,0 +1,3 @@ | |||
| @echo off | |||
| sc stop Tomcat8 | |||
| ping 127.0.0.1 > nul | |||
| @@ -0,0 +1,3 @@ | |||
| #!/bin/bash | |||
| source ./setenv.sh | |||
| bash ${tc_instance}/bin/catalina.sh stop >> /dev/null 2>&1 | |||
| @@ -0,0 +1,2 @@ | |||
| @echo off | |||
| sc start Tomcat8 | |||
| @@ -0,0 +1,3 @@ | |||
| #!/bin/bash | |||
| source ./setenv.sh | |||
| bash ${tc_instance}/bin/catalina.sh start >> /dev/null 2>&1 | |||
| @@ -0,0 +1,5 @@ | |||
| #!/bin/bash | |||
| cp src/main/webapp/WEB-INF/classes/log4j.properties.prod src/main/webapp/WEB-INF/classes/log4j.properties | |||
| cp src/main/webapp/WEB-INF/app.properties.prod src/main/webapp/WEB-INF/app.properties | |||
| rm -rf src/main/webapp/META-INF/context.xml | |||
| bash ./gradlew clean war | |||
| @@ -0,0 +1,12 @@ | |||
| #!/bin/bash | |||
| source ./setenv.sh | |||
| cp src/main/webapp/WEB-INF/classes/log4j.properties.local.mac src/main/webapp/WEB-INF/classes/log4j.properties | |||
| cp src/main/webapp/WEB-INF/app.properties.local.mac src/main/webapp/WEB-INF/app.properties | |||
| cp src/main/webapp/META-INF/context.xml.2fi src/main/webapp/META-INF/context.xml | |||
| #mvn clean package | |||
| #cp target/*.war ${tc_instance}/webapps/ | |||
| bash ./gradlew clean war | |||
| cp build/libs/*.war ${tc_instance}/webapps/ | |||
| @@ -0,0 +1,3 @@ | |||
| @echo off | |||
| set PROJECT_NAME=tbms | |||
| set TC_BASE=C:\Tomcat | |||
| @@ -0,0 +1,2 @@ | |||
| project=tbms | |||
| tc_instance=/Users/2fiadmin/tomcat | |||
| @@ -0,0 +1 @@ | |||
| rootProject.name = 'TBMS' | |||
| @@ -0,0 +1,135 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core; | |||
| import java.io.Serializable; | |||
| import java.util.Date; | |||
| import javax.persistence.Column; | |||
| import javax.persistence.GeneratedValue; | |||
| import javax.persistence.GenerationType; | |||
| import javax.persistence.Id; | |||
| import javax.persistence.MappedSuperclass; | |||
| import javax.persistence.Version; | |||
| @MappedSuperclass | |||
| public abstract class BaseEntity<PK extends Serializable> implements Serializable, Cloneable { | |||
| private static final long serialVersionUID = -7211267047474252631L; | |||
| @Id | |||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | |||
| private PK id; | |||
| @Column(columnDefinition = "int(11)") | |||
| private Integer sysGroupId; | |||
| @Version | |||
| @Column(columnDefinition = "mediumint unsigned", nullable = false) | |||
| private int versionId; // hibernate optimistic lock version id | |||
| @Column(columnDefinition = "boolean", nullable = false) | |||
| private boolean deleted; // defaults to false | |||
| @Column(updatable = false) | |||
| private Integer createdBy; // User ID that created this record | |||
| @Column | |||
| private Integer modifiedBy; // User ID that last modified this record | |||
| @Column(updatable = false, nullable = false) | |||
| private Date created; // date created | |||
| @Column(nullable = false) | |||
| private Date modified; // last modified date | |||
| /** Default constructor */ | |||
| public BaseEntity() { | |||
| } | |||
| /* | |||
| * implements Cloneable | |||
| */ | |||
| @Override | |||
| protected Object clone() throws CloneNotSupportedException { | |||
| BaseEntity<?> clone = (BaseEntity<?>) super.clone(); | |||
| clone.id = null; | |||
| return clone; | |||
| } | |||
| /* | |||
| * getters and setters | |||
| */ | |||
| public PK getId() { | |||
| return id; | |||
| } | |||
| @SuppressWarnings("unchecked") | |||
| public void setId(Serializable id) { | |||
| this.id = (PK) id; | |||
| } | |||
| public int getVersionId() { | |||
| return versionId; | |||
| } | |||
| public void setVersionId(int versionId) { | |||
| this.versionId = versionId; | |||
| } | |||
| public boolean isDeleted() { | |||
| return deleted; | |||
| } | |||
| public void setDeleted(boolean deleted) { | |||
| this.deleted = deleted; | |||
| } | |||
| public Integer getCreatedBy() { | |||
| return createdBy; | |||
| } | |||
| public void setCreatedBy(Integer createdBy) { | |||
| this.createdBy = createdBy; | |||
| } | |||
| public Integer getModifiedBy() { | |||
| return modifiedBy; | |||
| } | |||
| public void setModifiedBy(Integer modifiedBy) { | |||
| this.modifiedBy = modifiedBy; | |||
| } | |||
| public Date getCreated() { | |||
| return created; | |||
| } | |||
| public void setCreated(Date created) { | |||
| this.created = created; | |||
| } | |||
| public Date getModified() { | |||
| return modified; | |||
| } | |||
| public void setModified(Date modified) { | |||
| this.modified = modified; | |||
| } | |||
| public Integer getSysGroupId() { | |||
| return sysGroupId; | |||
| } | |||
| public void setSysGroupId(Integer sysGroupId) { | |||
| this.sysGroupId = sysGroupId; | |||
| } | |||
| } | |||
| @@ -0,0 +1,107 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core; | |||
| import java.io.Serializable; | |||
| import java.util.Date; | |||
| public interface IBaseEntity<PK extends Serializable> extends Serializable, Cloneable { | |||
| /** | |||
| * @return the entity id | |||
| */ | |||
| public PK getId(); | |||
| /** | |||
| * Set the entity id | |||
| * | |||
| * @param id | |||
| * the entity id | |||
| */ | |||
| public void setId(PK id); | |||
| /** | |||
| * @return hibernate version id | |||
| */ | |||
| public int getVersionId(); | |||
| /** | |||
| * Set the hibernate version id | |||
| * | |||
| * @param versionId | |||
| * the hibernate version id | |||
| */ | |||
| public void setVersionId(int versionId); | |||
| /** | |||
| * @return entity's deleted flag | |||
| */ | |||
| public boolean isDeleted(); | |||
| /** | |||
| * Set the entity's deleted flag | |||
| * | |||
| * @param deleted | |||
| * the entity's deleted flag | |||
| */ | |||
| public void setDeleted(boolean deleted); | |||
| /** | |||
| * @return created by User ID | |||
| */ | |||
| public Integer getCreatedBy(); | |||
| /** | |||
| * Set the created by User ID | |||
| * | |||
| * @param createdBy | |||
| * the created by User ID | |||
| */ | |||
| public void setCreatedBy(Integer createdBy); | |||
| /** | |||
| * @return the last modified by User ID | |||
| */ | |||
| public Integer getModifiedBy(); | |||
| /** | |||
| * Set the last modified by User ID | |||
| * | |||
| * @param modifiedBy | |||
| * the last modified by User ID | |||
| */ | |||
| public void setModifiedBy(Integer modifiedBy); | |||
| /** | |||
| * @return created date | |||
| */ | |||
| public Date getCreated(); | |||
| /** | |||
| * Set the created date | |||
| * | |||
| * @param created | |||
| * the created date | |||
| */ | |||
| public void setCreated(Date created); | |||
| /** | |||
| * @return the last modified date | |||
| */ | |||
| public Date getModified(); | |||
| /** | |||
| * Set the last modified date | |||
| * | |||
| * @param modified | |||
| * the last modified date | |||
| */ | |||
| public void setModified(Date modified); | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| /** | |||
| * Get or Set Session Attributes | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class Session { | |||
| public static final String AVAILABLE_LOCALES = "availableLocales"; | |||
| /** | |||
| * Get session attribute | |||
| */ | |||
| public static Object getAttribute(HttpServletRequest request, String name) { | |||
| return request.getSession().getAttribute(name); | |||
| } | |||
| /** | |||
| * Set session attribute | |||
| */ | |||
| public static void setAttribute(HttpServletRequest request, String name, Object value) { | |||
| request.getSession().setAttribute(name, value); | |||
| } | |||
| } | |||
| @@ -0,0 +1,149 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core; | |||
| import com.ffii.core.setting.service.SettingsService; | |||
| import com.ffii.core.utils.BooleanUtils; | |||
| public abstract class Settings { | |||
| /* | |||
| * System-wide settings | |||
| */ | |||
| /** Define all available language names as comma separated string */ | |||
| public static final String SYS_AVAILABLE_LANGUAGES = "SYS.availableLanguages"; | |||
| /** Define all available locales as comma separated string */ | |||
| public static final String SYS_AVAILABLE_LOCALES = "SYS.availableLocales"; | |||
| /** Define the system default locale as string */ | |||
| public static final String SYS_DEFAULT_LOCALE = "SYS.defaultLocale"; | |||
| /** Define the system available currencies as comma separated string */ | |||
| public static final String SYS_CURRENCIES = "SYS.currencies"; | |||
| /** Define the system modules (authorities.module) */ | |||
| public static final String SYS_ROLE_MODULES = "SYS.modules"; | |||
| /* | |||
| * Mail settings | |||
| */ | |||
| /** Mail - SMTP host */ | |||
| public static final String MAIL_SMTP_HOST = "MAIL.smtp.host"; | |||
| /** Mail - SMTP port */ | |||
| public static final String MAIL_SMTP_PORT = "MAIL.smtp.port"; | |||
| /** Mail - SMTP username */ | |||
| public static final String MAIL_SMTP_USERNAME = "MAIL.smtp.username"; | |||
| /** Mail - SMTP password */ | |||
| public static final String MAIL_SMTP_PASSWORD = "MAIL.smtp.password"; | |||
| public static final String SYS_PASSWORD_RULE_MIN = "SYS.password.rule.length.min"; | |||
| public static final String SYS_PASSWORD_RULE_MAX = "SYS.password.rule.length.max"; | |||
| public static final String SYS_PASSWORD_RULE_NUMBER = "SYS.password.rule.number"; | |||
| public static final String SYS_PASSWORD_RULE_UPPER_ENG = "SYS.password.rule.upper.eng"; | |||
| public static final String SYS_PASSWORD_RULE_LOWER_ENG = "SYS.password.rule.lower.eng"; | |||
| public static final String SYS_PASSWORD_RULE_SPECIAL = "SYS.password.rule.special"; | |||
| public static final String JS_VERSION = "JS.version"; | |||
| public static class MailSMTP { | |||
| private String host; | |||
| private String port; | |||
| private String username; | |||
| private String password; | |||
| public MailSMTP(SettingsService settingsService) { | |||
| if (settingsService == null) throw new NullPointerException("SettingsService cannot be null"); | |||
| this.host = settingsService.getString(MAIL_SMTP_HOST); | |||
| this.port = settingsService.getString(MAIL_SMTP_PORT); | |||
| this.username = settingsService.getString(MAIL_SMTP_USERNAME); | |||
| this.password = settingsService.getString(MAIL_SMTP_PASSWORD); | |||
| } | |||
| public String getHost() { | |||
| return host; | |||
| } | |||
| public String getPort() { | |||
| return port; | |||
| } | |||
| public String getUsername() { | |||
| return username; | |||
| } | |||
| public String getPassword() { | |||
| return password; | |||
| } | |||
| } | |||
| public static class PasswordRule { | |||
| private int min; | |||
| private int max; | |||
| private boolean number; | |||
| private boolean upperEng; | |||
| private boolean lowerEng; | |||
| private boolean specialChar; | |||
| public PasswordRule(SettingsService settingsService) { | |||
| if (settingsService == null) throw new NullPointerException("SettingsService cannot be null"); | |||
| this.min = settingsService.getInt(SYS_PASSWORD_RULE_MIN); | |||
| this.max = settingsService.getInt(SYS_PASSWORD_RULE_MAX); | |||
| this.number = BooleanUtils.isTrue(settingsService.getString(SYS_PASSWORD_RULE_NUMBER)); | |||
| this.upperEng = BooleanUtils.isTrue(settingsService.getString(SYS_PASSWORD_RULE_UPPER_ENG)); | |||
| this.lowerEng = BooleanUtils.isTrue(settingsService.getString(SYS_PASSWORD_RULE_LOWER_ENG)); | |||
| this.specialChar = BooleanUtils.isTrue(settingsService.getString(SYS_PASSWORD_RULE_SPECIAL)); | |||
| } | |||
| public int getMin() { | |||
| return min; | |||
| } | |||
| public int getMax() { | |||
| return max; | |||
| } | |||
| public boolean needNumberChar() { | |||
| return number; | |||
| } | |||
| public boolean needUpperEngChar() { | |||
| return upperEng; | |||
| } | |||
| public boolean needLowerEngChar() { | |||
| return lowerEng; | |||
| } | |||
| public boolean needSpecialChar() { | |||
| return specialChar; | |||
| } | |||
| public String getWrongMsg() { | |||
| StringBuilder msg = new StringBuilder("Please Following Password Rule.\n"); | |||
| msg.append("Minimum " + getMin() + " Characters\n"); | |||
| msg.append("Maximum " + getMax() + " Characters\n"); | |||
| if (needNumberChar()) msg.append("Numbers\n"); | |||
| if (needLowerEngChar()) msg.append("Lower-Case Letters\n"); | |||
| if (needUpperEngChar()) msg.append("Capital Letters\n"); | |||
| if (needSpecialChar()) msg.append("Symbols\n"); | |||
| return msg.toString(); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,264 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core; | |||
| import java.util.List; | |||
| import javax.persistence.Column; | |||
| import javax.persistence.Entity; | |||
| import javax.persistence.Table; | |||
| import javax.persistence.Transient; | |||
| import org.springframework.security.core.GrantedAuthority; | |||
| import org.springframework.security.core.userdetails.UserDetails; | |||
| @Entity | |||
| @Table(name = "users") | |||
| public class User extends BaseEntity<Integer> implements UserDetails { | |||
| private static final long serialVersionUID = -9036483598847846102L; | |||
| /** Company ID */ | |||
| @Column(nullable = false) | |||
| private int companyId; | |||
| /** Customer ID, optional, for End User's customers */ | |||
| @Column(nullable = true) | |||
| private Integer customerId; | |||
| /** username for login */ | |||
| @Column(columnDefinition = "varchar(32)", nullable = false) | |||
| private String username; | |||
| /** password for login */ | |||
| @Column(columnDefinition = "varchar(64)", nullable = false) | |||
| private String password; | |||
| /** is user expired? (not implemented) */ | |||
| @Column(columnDefinition = "boolean", nullable = false) | |||
| private boolean expired; | |||
| /** is user locked? (not implemented) */ | |||
| @Column(columnDefinition = "boolean", nullable = false) | |||
| private boolean locked; | |||
| /** User default locale (en / zh_TW / zh_CN) */ | |||
| @Column(columnDefinition = "varchar(5)") | |||
| private String locale; | |||
| /** Full name for display */ | |||
| @Column(columnDefinition = "varchar(90)") | |||
| private String fullname; | |||
| /* User profile info (optional fields) START */ | |||
| /** First name */ | |||
| @Column(columnDefinition = "varchar(45)") | |||
| private String firstname; | |||
| /** Last name */ | |||
| @Column(columnDefinition = "varchar(30)") | |||
| private String lastname; | |||
| /** Department */ | |||
| @Column(nullable = true) | |||
| private Integer deptId; | |||
| /** Job title */ | |||
| @Column(columnDefinition = "varchar(60)") | |||
| private String title; | |||
| /** Email */ | |||
| @Column(columnDefinition = "varchar(120)") | |||
| private String email; | |||
| /** Phone 1 */ | |||
| @Column(columnDefinition = "varchar(30)") | |||
| private String phone1; | |||
| /** Phone 2 */ | |||
| @Column(columnDefinition = "varchar(30)") | |||
| private String phone2; | |||
| /** Remarks */ | |||
| @Column(columnDefinition = "varchar(600)") | |||
| private String remarks; | |||
| /* User profile info (optional fields) END */ | |||
| /** Transient authorities (populated during user authentication) */ | |||
| @Transient | |||
| private List<GrantedAuthority> authorities; | |||
| /* | |||
| * getters and setters | |||
| */ | |||
| public int getCompanyId() { | |||
| return companyId; | |||
| } | |||
| public void setCompanyId(int companyId) { | |||
| this.companyId = companyId; | |||
| } | |||
| public Integer getCustomerId() { | |||
| return customerId; | |||
| } | |||
| public void setCustomerId(Integer customerId) { | |||
| this.customerId = customerId; | |||
| } | |||
| public String getUsername() { | |||
| return username; | |||
| } | |||
| public void setUsername(String username) { | |||
| this.username = username; | |||
| } | |||
| public String getPassword() { | |||
| return password; | |||
| } | |||
| public void setPassword(String password) { | |||
| this.password = password; | |||
| } | |||
| public boolean isExpired() { | |||
| return expired; | |||
| } | |||
| public void setExpired(boolean expired) { | |||
| this.expired = expired; | |||
| } | |||
| public boolean isLocked() { | |||
| return locked; | |||
| } | |||
| public void setLocked(boolean locked) { | |||
| this.locked = locked; | |||
| } | |||
| public String getLocale() { | |||
| return locale; | |||
| } | |||
| public void setLocale(String locale) { | |||
| this.locale = locale; | |||
| } | |||
| public String getFullname() { | |||
| return fullname; | |||
| } | |||
| public void setFullname(String fullname) { | |||
| this.fullname = fullname; | |||
| } | |||
| public String getFirstname() { | |||
| return firstname; | |||
| } | |||
| public void setFirstname(String firstname) { | |||
| this.firstname = firstname; | |||
| } | |||
| public String getLastname() { | |||
| return lastname; | |||
| } | |||
| public void setLastname(String lastname) { | |||
| this.lastname = lastname; | |||
| } | |||
| public Integer getDeptId() { | |||
| return deptId; | |||
| } | |||
| public void setDeptId(Integer deptId) { | |||
| this.deptId = deptId; | |||
| } | |||
| public String getTitle() { | |||
| return title; | |||
| } | |||
| public void setTitle(String title) { | |||
| this.title = title; | |||
| } | |||
| public String getEmail() { | |||
| return email; | |||
| } | |||
| public void setEmail(String email) { | |||
| this.email = email; | |||
| } | |||
| public String getPhone1() { | |||
| return phone1; | |||
| } | |||
| public void setPhone1(String phone1) { | |||
| this.phone1 = phone1; | |||
| } | |||
| public String getPhone2() { | |||
| return phone2; | |||
| } | |||
| public void setPhone2(String phone2) { | |||
| this.phone2 = phone2; | |||
| } | |||
| public String getRemarks() { | |||
| return remarks; | |||
| } | |||
| public void setRemarks(String remarks) { | |||
| this.remarks = remarks; | |||
| } | |||
| public void setAuthorities(List<GrantedAuthority> authorities) { | |||
| this.authorities = authorities; | |||
| } | |||
| /* | |||
| * Override | |||
| */ | |||
| @Override | |||
| public List<GrantedAuthority> getAuthorities() { | |||
| return authorities; | |||
| } | |||
| @Override | |||
| public boolean isAccountNonExpired() { | |||
| return !expired; | |||
| } | |||
| @Override | |||
| public boolean isAccountNonLocked() { | |||
| return !locked; | |||
| } | |||
| @Override | |||
| public boolean isCredentialsNonExpired() { | |||
| return true; | |||
| } | |||
| @Override | |||
| public boolean isEnabled() { | |||
| return true; | |||
| } | |||
| } | |||
| @@ -0,0 +1,160 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.dao; | |||
| import java.io.Serializable; | |||
| import java.util.Date; | |||
| import java.util.List; | |||
| import com.ffii.core.BaseEntity; | |||
| import com.ffii.core.User; | |||
| import com.ffii.core.utils.SecurityUtils; | |||
| import org.hibernate.query.Query; | |||
| import org.springframework.orm.hibernate5.support.HibernateDaoSupport; | |||
| /** | |||
| * Implementation of the {@link IHibernateDao} interface. | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class HibernateDao<T extends BaseEntity<PK>, PK extends Serializable> extends HibernateDaoSupport | |||
| implements IHibernateDao<T, PK> { | |||
| private final Class<T> type; | |||
| public HibernateDao(final Class<T> type) { | |||
| this.type = type; | |||
| } | |||
| /** | |||
| * Automatically set the {@code createdBy}, {@code created}, {@code modifiedBy}, | |||
| * and {@code modified} fields of the object instance. | |||
| * | |||
| * @param instance the object instance | |||
| * @param isNew {@code true} if the instance is new, and need to set the | |||
| * {@code createdBy} and {@code created} fields | |||
| */ | |||
| private void autoSetCreateAndLastModifyFields(final T instance, boolean isNew) { | |||
| // try to get the authenticated User ID from session (not available in Mapp) | |||
| User user = SecurityUtils.getUser(); | |||
| Integer userId = null; | |||
| Integer sysGroupId = null; | |||
| if (user != null){ | |||
| userId = user.getId(); | |||
| sysGroupId = user.getSysGroupId(); | |||
| } | |||
| Date now = new Date(); | |||
| if (isNew) { | |||
| if (instance.getCreatedBy() == null && userId != null) | |||
| instance.setCreatedBy(userId); | |||
| if (userId != null) | |||
| instance.setModifiedBy(userId); | |||
| if (instance.getCreated() == null) | |||
| instance.setCreated(now); | |||
| if(instance.getSysGroupId() == null) | |||
| instance.setSysGroupId(sysGroupId); | |||
| instance.setModified(now); | |||
| } else { | |||
| if (userId != null) | |||
| instance.setModifiedBy(userId); | |||
| instance.setModified(now); | |||
| } | |||
| } | |||
| @SuppressWarnings("unchecked") | |||
| @Override | |||
| public PK save(final T instance) { | |||
| autoSetCreateAndLastModifyFields(instance, true); | |||
| return (PK) getHibernateTemplate().save(instance); | |||
| } | |||
| @SuppressWarnings("unchecked") | |||
| @Override | |||
| public PK saveAs(final T instance) { | |||
| return (PK) getHibernateTemplate().save(instance); | |||
| } | |||
| @Override | |||
| public void update(final T instance) { | |||
| autoSetCreateAndLastModifyFields(instance, false); | |||
| getHibernateTemplate().update(instance); | |||
| } | |||
| @Override | |||
| public PK saveOrUpdate(final T instance) { | |||
| Serializable id = instance.getId(); | |||
| if (id instanceof Long) { | |||
| if (((Long) id).longValue() < 0L) | |||
| id = null; | |||
| } else if (id instanceof Integer) { | |||
| if (((Integer) id).intValue() < 0) | |||
| id = null; | |||
| } | |||
| if (id == null) | |||
| return save(instance); | |||
| else { | |||
| update(instance); | |||
| return (PK) instance.getId(); | |||
| } | |||
| } | |||
| @Override | |||
| public boolean delete(final T instance) { | |||
| try { | |||
| getHibernateTemplate().delete(instance); | |||
| return true; | |||
| } catch (Exception e) { | |||
| return false; | |||
| } | |||
| } | |||
| @Override | |||
| public void deleteAll(List<T> instances) { | |||
| getHibernateTemplate().deleteAll(instances); | |||
| } | |||
| @Override | |||
| public void flush() { | |||
| getHibernateTemplate().flush(); | |||
| } | |||
| @Override | |||
| public T find(final PK id) { | |||
| return getHibernateTemplate().get(type, id); | |||
| } | |||
| @Override | |||
| @SuppressWarnings("unchecked") | |||
| public T findByQuery(final String queryString, final Serializable... args) { | |||
| return getHibernateTemplate().execute(session -> { | |||
| Query<T> query = session.createQuery(queryString); | |||
| for (int i = 0; i < args.length; i++) { | |||
| query.setParameter(i, args[i]); | |||
| } | |||
| query.setMaxResults(1); | |||
| return query.uniqueResult(); | |||
| }); | |||
| } | |||
| @Override | |||
| @SuppressWarnings("unchecked") | |||
| public List<T> listByQuery(final String queryString, final Serializable... args) { | |||
| return getHibernateTemplate().execute(session -> { | |||
| Query<T> query = session.createQuery(queryString); | |||
| for (int i = 0; i < args.length; i++) { | |||
| query.setParameter(i, args[i]); | |||
| } | |||
| return query.list(); | |||
| }); | |||
| } | |||
| } | |||
| @@ -0,0 +1,125 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.dao; | |||
| import java.io.Serializable; | |||
| import java.util.List; | |||
| import org.springframework.orm.hibernate5.HibernateTemplate; | |||
| /** | |||
| * Hibernate DAO interface. For use with business services to perform actual operations. | |||
| * | |||
| * @param <T> | |||
| * The class this DAO is taking care of. | |||
| * @param <PK> | |||
| * The type of the primary key of class instances. | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public interface IHibernateDao<T extends Serializable, PK extends Serializable> { | |||
| /** | |||
| * Returns the underlying {@link HibernateTemplate} | |||
| */ | |||
| public HibernateTemplate getHibernateTemplate(); | |||
| /** | |||
| * Saves the instance to the database. | |||
| * | |||
| * @param instance | |||
| * the object instance to save | |||
| * | |||
| * @return the object instance's primary key in database | |||
| */ | |||
| public PK save(T instance); | |||
| /** | |||
| * Saves the instance to the database without setting the created/updated fields. | |||
| * | |||
| * @param instance | |||
| * the object instance to save | |||
| * | |||
| * @return the object instance's primary key in database | |||
| */ | |||
| public PK saveAs(T instance); | |||
| /** | |||
| * Updates the record in the database with the given instance. | |||
| * | |||
| * @param instance | |||
| * modified object instance | |||
| */ | |||
| public void update(T instance); | |||
| /** | |||
| * Saves or updates the record in the database with the given instance. | |||
| * | |||
| * @param instance | |||
| * new or modified object instance | |||
| * | |||
| * @return the object instance's primary key in database | |||
| */ | |||
| public PK saveOrUpdate(T instance); | |||
| /** | |||
| * Deletes the object instance from the database. | |||
| * | |||
| * @param instance | |||
| * the object instance to remove | |||
| */ | |||
| public boolean delete(T instance); | |||
| /** | |||
| * Remove a collection of instances from the database. | |||
| * | |||
| * @param instances | |||
| * The object instances to remove | |||
| */ | |||
| public void deleteAll(List<T> instances); | |||
| /** | |||
| * Finds the object with the specified identity from the database. | |||
| * | |||
| * @param id | |||
| * the object's primary key in database | |||
| * | |||
| * @return the mapped object instance from the database | |||
| */ | |||
| public T find(PK id); | |||
| /** | |||
| * Finds the object using the specified query (HQL) and arguments from the database. | |||
| * | |||
| * @param queryString | |||
| * the query string using HQL | |||
| * @param args | |||
| * variable number of arguments to pass to the query. Note you must have the exact number of arguments as specified in the query. | |||
| * | |||
| * @return the object instance from the database | |||
| */ | |||
| public T findByQuery(String queryString, Serializable... args); | |||
| /** | |||
| * Returns a {@code List} of {@code Object}s using the specified query (HQL) and arguments from the database. | |||
| * | |||
| * @param queryString | |||
| * the query string using HQL | |||
| * @param args | |||
| * variable number of arguments to pass to the query. Note you must have the exact number of arguments as specified in the query. | |||
| * | |||
| * @return {@code List} of object instances from the database | |||
| */ | |||
| public List<T> listByQuery(String queryString, Serializable... args); | |||
| public void flush(); | |||
| } | |||
| @@ -0,0 +1,260 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.dao; | |||
| import java.io.Serializable; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import javax.persistence.Table; | |||
| import com.ffii.core.BaseEntity; | |||
| import com.ffii.core.utils.MapUtils; | |||
| import com.ffii.core.utils.Params; | |||
| import org.springframework.dao.IncorrectResultSizeDataAccessException; | |||
| import org.springframework.jdbc.core.BeanPropertyRowMapper; | |||
| import org.springframework.jdbc.core.JdbcOperations; | |||
| import org.springframework.jdbc.core.JdbcTemplate; | |||
| import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport; | |||
| import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; | |||
| import org.springframework.jdbc.core.simple.SimpleJdbcInsert; | |||
| /** | |||
| * JDBC DAO implementation which extends Spring {@link NamedParameterJdbcDaoSupport} | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class JdbcDao extends NamedParameterJdbcDaoSupport { | |||
| /** | |||
| * Return an instance of SimpleJdbcInsert with the specified table name | |||
| */ | |||
| private SimpleJdbcInsert getJdbcInsert(String tableName, String idName) { | |||
| SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(getJdbcTemplate()); | |||
| if (idName != null) | |||
| return jdbcInsert.withTableName(tableName).usingGeneratedKeyColumns(idName); | |||
| else | |||
| return jdbcInsert.withTableName(tableName); | |||
| } | |||
| /** | |||
| * Query for a {@code List} of {@code Object}s of type {@code T} using the supplied {@code class} to map the query results. Uses SQL with the named | |||
| * parameter support provided by the {@code NamedParameterJdbcTemplate}. | |||
| * | |||
| * @param sql | |||
| * the SQL query to run | |||
| * @param mappedClass | |||
| * the class to use for result mapping | |||
| * @param args | |||
| * the map containing the arguments for the query | |||
| * @param maxRows | |||
| * the maximum results to return | |||
| */ | |||
| public <T> List<T> query(String sql, Class<T> mappedClass, Map<String, ?> args, int maxRows) { | |||
| ((JdbcTemplate) getNamedParameterJdbcTemplate().getJdbcOperations()).setMaxRows(maxRows); | |||
| return getNamedParameterJdbcTemplate().query(sql, args, BeanPropertyRowMapper.newInstance(mappedClass)); | |||
| } | |||
| /** | |||
| * Execute the supplied query with the supplied arguments. | |||
| * <p> | |||
| * The query is expected to be a single row query (otherwise, {@code null} will be returned); the result row will be mapped to a Map<String, Object> (one | |||
| * entry for each column, using the column name as the key). Uses SQL with the named parameter support provided by the {@link NamedParameterJdbcTemplate} | |||
| * </p> | |||
| * | |||
| * @param sql | |||
| * the SQL query to run | |||
| * @param args | |||
| * the map containing the arguments for the query | |||
| */ | |||
| public Map<String, Object> queryForMap(String sql, Map<String, ?> args) { | |||
| try { | |||
| return getNamedParameterJdbcTemplate().queryForMap(sql, args); | |||
| } catch (IncorrectResultSizeDataAccessException e) { | |||
| logger.debug("Unexpected row count for queryForMap()", e); | |||
| return null; | |||
| } | |||
| } | |||
| /** | |||
| * Query for a {@code List} of {@code Map}s, each element in the returned {@code List} is constructed as a {@code Map} as described in | |||
| * {@link JdbcOperations#queryForMap}. Uses SQL with the named parameter support provided by the {@code NamedParameterJdbcTemplate}. | |||
| * | |||
| * @param sql | |||
| * the SQL query to run | |||
| * @param args | |||
| * the map containing the arguments for the query | |||
| * @param maxRows | |||
| * the maximum results to return | |||
| */ | |||
| public List<Map<String, Object>> queryForList(String sql, Map<String, ?> args, int maxRows) { | |||
| ((JdbcTemplate) getNamedParameterJdbcTemplate().getJdbcOperations()).setMaxRows(maxRows); | |||
| return getNamedParameterJdbcTemplate().queryForList(sql, args); | |||
| } | |||
| /** | |||
| * Query for a {@code List} of {@code Map}s, each element in the returned {@code List} is constructed as a {@code Map} as described in | |||
| * {@link JdbcOperations#queryForMap}. Uses SQL with the named parameter support provided by the {@code NamedParameterJdbcTemplate}. | |||
| * | |||
| * @param sql | |||
| * the SQL query to run | |||
| * @param args | |||
| * the map containing the arguments for the query | |||
| */ | |||
| public List<Map<String, Object>> queryForList(String sql, Map<String, ?> args) { | |||
| return queryForList(sql, args, 0); | |||
| } | |||
| /** | |||
| * Query for an {@code int} passing in a SQL query using the named parameter support provided by the {@code NamedParameterJdbcTemplate} and a map containing | |||
| * the arguments. | |||
| * <p> | |||
| * The query is expected to be a single row/single column query; the returned result will be directly mapped to an {@code int}. | |||
| * | |||
| * @param sql | |||
| * the SQL query to run | |||
| * @param args | |||
| * the map containing the arguments for the query | |||
| */ | |||
| public int queryForInt(String sql, Map<String, ?> args) { | |||
| Integer value = getNamedParameterJdbcTemplate().queryForObject(sql, args, Integer.class); | |||
| return (value != null ? value.intValue() : 0); | |||
| } | |||
| /** | |||
| * Query for a {@code String} passing in a SQL query using the named parameter support provided by the {@code NamedParameterJdbcTemplate} and a map | |||
| * containing the arguments. | |||
| * <p> | |||
| * The query is expected to be a single row/single column query; the returned result will be directly mapped to a {@code String}. | |||
| * | |||
| * @param sql | |||
| * the SQL query to run | |||
| * @param args | |||
| * the map containing the arguments for the query | |||
| */ | |||
| public String queryForString(String sql, Map<String, ?> args) { | |||
| return getNamedParameterJdbcTemplate().queryForObject(sql, args, String.class); | |||
| } | |||
| /** | |||
| * Executes a batch using the supplied SQL statement with the batch of supplied arguments using the named parameter support provided by the | |||
| * {@code NamedParameterJdbcTemplate} | |||
| * | |||
| * @param sql | |||
| * the SQL statement to execute | |||
| * @param batchValues | |||
| * the array of Maps containing the batch of arguments for the query | |||
| * | |||
| * @return an array containing the numbers of rows affected by each update in the batch | |||
| */ | |||
| public int[] executeBatchUpdate(String sql, Map<String, ?>[] batchValues) { | |||
| return getNamedParameterJdbcTemplate().batchUpdate(sql, batchValues); | |||
| } | |||
| /** | |||
| * Execute the supplied SQL statement with (optional) supplied arguments using the named parameter support provided by the | |||
| * {@code NamedParameterJdbcTemplate} | |||
| * | |||
| * @param sql | |||
| * the SQL statement to execute | |||
| * @param args | |||
| * the map containing the arguments for the query | |||
| * | |||
| * @return the number of rows affected by the update | |||
| */ | |||
| public int executeUpdate(String sql, Map<String, ?> args) { | |||
| return getNamedParameterJdbcTemplate().update(sql, args); | |||
| } | |||
| /** | |||
| * Execute the supplied SQL statement with no arguments | |||
| * | |||
| * @param sql | |||
| * the SQL statement to execute | |||
| * | |||
| * @return the number of rows affected by the update | |||
| */ | |||
| public int executeUpdate(String sql) { | |||
| return getJdbcTemplate().update(sql); | |||
| } | |||
| /** | |||
| * Executes SQL UPDATE statement with the supplied class and its ID and keyValuePairs | |||
| * | |||
| * @param clazz | |||
| * any class that extends BaseEntity | |||
| * @param keyValuePairs | |||
| * key value pairs, must include the ID field | |||
| * | |||
| * @return the number of rows affected by the update | |||
| */ | |||
| public int executeUpdateByTable(String tableName, Map<String, ?> args) { | |||
| StringBuilder sql = new StringBuilder("UPDATE `" + tableName + "` SET "); | |||
| int i = 0; | |||
| for (String key : args.keySet()) { | |||
| if (i > 0) sql.append(", "); | |||
| sql.append(key + " = :" + key); | |||
| i++; | |||
| } | |||
| sql.append(" WHERE id = :id"); | |||
| return executeUpdate(sql.toString(), args); | |||
| } | |||
| /** | |||
| * Execute SQL Insert statement with the supplied arguments | |||
| * | |||
| * @param tableName | |||
| * the database table name | |||
| * @param args | |||
| * the map containing the arguments for the insert statement in the form of column name and its value | |||
| * | |||
| * @return the number of rows affected as returned by the JDBC driver | |||
| */ | |||
| public int executeInsert(String tableName, Map<String, Object> args) { | |||
| return getJdbcInsert(tableName, null).execute(args); | |||
| } | |||
| /** | |||
| * Execute SQL Insert statement with the supplied arguments and return the generated key value | |||
| * | |||
| * @param tableName | |||
| * the database table name | |||
| * @param idName | |||
| * the name of ID column that has auto generated key | |||
| * @param args | |||
| * the map containing the arguments for the insert statement in the form of column name and its value | |||
| * | |||
| * @return the generated key value | |||
| */ | |||
| public Number executeInsertAndReturnKey(String tableName, String idName, Map<String, Object> args) { | |||
| return getJdbcInsert(tableName, idName).executeAndReturnKey(args); | |||
| } | |||
| /** | |||
| * Executes SQL Delete statement with the supplied class and its ID | |||
| * | |||
| * @param clazz | |||
| * any class that extends BaseEntity | |||
| * @param id | |||
| * the entity's ID | |||
| * | |||
| * @return the number of rows affected by the delete | |||
| */ | |||
| public int executeDelete(Class<? extends BaseEntity<Serializable>> clazz, Serializable id) { | |||
| Table table = clazz.getAnnotation(Table.class); | |||
| String sql = "DELETE FROM " + table.name() + " WHERE id = :id"; | |||
| return executeUpdate(sql, MapUtils.toHashMap(Params.ID, id)); | |||
| } | |||
| } | |||
| @@ -0,0 +1,56 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.engine; | |||
| import java.io.IOException; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Component; | |||
| import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; | |||
| import org.springframework.web.context.support.WebApplicationObjectSupport; | |||
| import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; | |||
| import freemarker.template.Template; | |||
| import freemarker.template.TemplateException; | |||
| /** | |||
| * FreeMarker template engine - a Spring Component Bean | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| @Component | |||
| public class FreeMarkerEngine extends WebApplicationObjectSupport { | |||
| @Autowired | |||
| private FreeMarkerConfigurer freemarkerConfig; | |||
| /** | |||
| * Generates content from FreeMarker template | |||
| * | |||
| * @param template | |||
| * the FreeMarker template name (e.g. {@code "example/example.ftl"}) | |||
| * @param model | |||
| * the model to be applied to the FreeMarker template, may be null | |||
| * @return the generated content as a {@code String}, or {@code null} if exceptions occured | |||
| */ | |||
| public String generateFreeMarkerContent(String template, Object model) { | |||
| try { | |||
| Template t = freemarkerConfig.getConfiguration().getTemplate(template); | |||
| return FreeMarkerTemplateUtils.processTemplateIntoString(t, model); | |||
| } catch (TemplateException e) { | |||
| logger.error("Error while processing FreeMarker template ", e); | |||
| } catch (IOException e) { | |||
| logger.error("IOException occured ", e); | |||
| } | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,316 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2018 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.engine; | |||
| import java.io.File; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import java.util.Properties; | |||
| import javax.mail.MessagingException; | |||
| import javax.mail.internet.InternetAddress; | |||
| import javax.mail.internet.MimeMessage; | |||
| import com.ffii.core.Settings; | |||
| import com.ffii.core.setting.service.SettingsService; | |||
| import com.ffii.core.utils.SecurityUtils; | |||
| import com.ffii.tbms.user.service.SysGroupService; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.core.io.InputStreamSource; | |||
| import org.springframework.mail.javamail.JavaMailSender; | |||
| import org.springframework.mail.javamail.JavaMailSenderImpl; | |||
| import org.springframework.mail.javamail.MimeMessageHelper; | |||
| import org.springframework.stereotype.Component; | |||
| import org.springframework.web.context.support.WebApplicationObjectSupport; | |||
| /** | |||
| * Mail Engine is a Spring Component Bean that handles E-mail operations. | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| @Component | |||
| public class MailEngine extends WebApplicationObjectSupport { | |||
| @Autowired | |||
| private FreeMarkerEngine freeMarkerEngine; | |||
| private JavaMailSender mailSender; | |||
| @Autowired | |||
| private SettingsService settingsService; | |||
| @Autowired | |||
| private SysGroupService sysGroupService; | |||
| /** | |||
| * Send HTML mail message | |||
| * | |||
| * @param from | |||
| * the from address, must not be null | |||
| * @param replyTo | |||
| * the reply to address, may be null | |||
| * @param to | |||
| * the to addresses, must not be null | |||
| * @param cc | |||
| * the cc addresses, may be null | |||
| * @param bcc | |||
| * the bcc addresses, may be null | |||
| * @param subject | |||
| * the subject, may be null | |||
| * @param html | |||
| * the html message body, must not be null | |||
| * @param attachmentFilename | |||
| * the name of the attachment as it will appear in the mail | |||
| * @param file | |||
| * the File to attach | |||
| */ | |||
| private JavaMailSender getJavaMailSender(int sysGroupId){ | |||
| //if(mailSender == null){ | |||
| Map<String, Object> sysGroup = sysGroupService.find(sysGroupId); | |||
| String smtp_host = ""; | |||
| int smtp_port = 0; | |||
| String smtp_username = ""; | |||
| String smtp_password = ""; | |||
| logger.info(sysGroup); | |||
| if(sysGroup != null){ | |||
| smtp_host = sysGroup.get("smtp_host")==null?"":sysGroup.get("smtp_host").toString(); | |||
| smtp_port = sysGroup.get("smtp_port")==null?0:Integer.parseInt(sysGroup.get("smtp_port").toString()); | |||
| smtp_username = sysGroup.get("smtp_username")==null?"":sysGroup.get("smtp_username").toString(); | |||
| smtp_password = sysGroup.get("smtp_password")==null?"":sysGroup.get("smtp_password").toString(); | |||
| } | |||
| JavaMailSenderImpl jmsi = new JavaMailSenderImpl(); | |||
| jmsi.setHost(smtp_host); | |||
| jmsi.setPort(smtp_port); | |||
| jmsi.setUsername(smtp_username); | |||
| jmsi.setPassword(smtp_password); | |||
| Properties props = jmsi.getJavaMailProperties(); | |||
| props.put("mail.transport.protocol", "smtp"); | |||
| props.put("mail.smtp.auth", "true"); | |||
| props.put("mail.smtp.starttbms.enable", "true"); | |||
| props.put("mail.debug", "true"); | |||
| mailSender = jmsi; | |||
| //} | |||
| return mailSender; | |||
| } | |||
| public void sendHtmlMail(int sysGroupId, InternetAddress from, InternetAddress replyTo, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, | |||
| String subject, String html, String attachmentFilename, File file) { | |||
| MimeMessage mimeMessage = getJavaMailSender(sysGroupId).createMimeMessage(); | |||
| try { | |||
| MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8"); | |||
| message.setFrom(from); | |||
| if (replyTo != null) message.setReplyTo(replyTo); | |||
| message.setTo(to); | |||
| if (cc != null) message.setCc(cc); | |||
| if (bcc != null) message.setBcc(bcc); | |||
| if (subject != null) message.setSubject(subject); | |||
| message.setText(html, true); | |||
| if (attachmentFilename != null && file != null) message.addAttachment(attachmentFilename, file); | |||
| } catch (MessagingException e) { | |||
| logger.error("Failed to send HTML mail", e); | |||
| } | |||
| getJavaMailSender(sysGroupId).send(mimeMessage); | |||
| } | |||
| /** | |||
| * Send HTML mail message | |||
| * | |||
| * @param from | |||
| * the from address, must not be null | |||
| * @param replyTo | |||
| * the reply to address, may be null | |||
| * @param to | |||
| * the to addresses, must not be null | |||
| * @param subject | |||
| * the subject, may be null | |||
| * @param html | |||
| * the html message body, must not be null | |||
| * @param attachmentFilename | |||
| * the name of the attachment as it will appear in the mail | |||
| * @param file | |||
| * the File to attach | |||
| */ | |||
| public void sendHtmlMail(int sysGroupId, InternetAddress from, InternetAddress replyTo, InternetAddress[] to, | |||
| String subject, String html, String attachmentFilename, File file) { | |||
| sendHtmlMail(sysGroupId, from, replyTo, to, null, null, subject, html, attachmentFilename, file); | |||
| } | |||
| /** | |||
| * Send HTML mail message | |||
| * | |||
| * @param from | |||
| * the from address, must not be null | |||
| * @param replyTo | |||
| * the reply to address, may be null | |||
| * @param to | |||
| * the to addresses, must not be null | |||
| * @param subject | |||
| * the subject, may be null | |||
| * @param html | |||
| * the html message body, must not be null | |||
| */ | |||
| public void sendHtmlMail(int sysGroupId,InternetAddress from, InternetAddress replyTo, InternetAddress[] to, String subject, String html) { | |||
| sendHtmlMail(sysGroupId, from, replyTo, to, null, null, subject, html, null, null); | |||
| } | |||
| /** | |||
| * Send HTML mail message generated from FreeMarker template | |||
| * | |||
| * @param from | |||
| * the from address, must not be null | |||
| * @param replyTo | |||
| * the reply to address, may be null | |||
| * @param to | |||
| * the to addresses, must not be null | |||
| * @param cc | |||
| * the cc addresses, may be null | |||
| * @param bcc | |||
| * the bcc addresses, may be null | |||
| * @param subject | |||
| * the subject, may be null | |||
| * @param template | |||
| * the FreeMarker template name, must not be null | |||
| * @param model | |||
| * the model to be applied to the FreeMarker template, may be null | |||
| * @param attachmentFilename | |||
| * the name of the attachment as it will appear in the mail | |||
| * @param file | |||
| * the File to attach | |||
| */ | |||
| public void sendFreeMarkerHtmlMail(int sysGroupId,InternetAddress from, InternetAddress replyTo, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, | |||
| String subject, String template, Map<?, ?> model, String attachmentFilename, File file) { | |||
| String html = freeMarkerEngine.generateFreeMarkerContent(template, model); | |||
| sendHtmlMail(sysGroupId,from, replyTo, to, cc, bcc, subject, html, attachmentFilename, file); | |||
| } | |||
| /** | |||
| * Send HTML mail message generated from FreeMarker template | |||
| * | |||
| * @param from | |||
| * the from address, must not be null | |||
| * @param replyTo | |||
| * the reply to address, may be null | |||
| * @param to | |||
| * the to addresses, must not be null | |||
| * @param subject | |||
| * the subject, may be null | |||
| * @param template | |||
| * the FreeMarker template name, must not be null | |||
| * @param model | |||
| * the model to be applied to the FreeMarker template, may be null | |||
| */ | |||
| public void sendFreeMarkerHtmlMail(int sysGroupId, InternetAddress from, InternetAddress replyTo, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, | |||
| String subject, String template, Map<?, ?> model) { | |||
| sendFreeMarkerHtmlMail(sysGroupId, from, replyTo, to, cc, bcc, subject, template, model, null, null); | |||
| } | |||
| /** | |||
| * Send HTML mail message generated from FreeMarker template | |||
| * | |||
| * @param from | |||
| * the from address, must not be null | |||
| * @param replyTo | |||
| * the reply to address, may be null | |||
| * @param to | |||
| * the to addresses, must not be null | |||
| * @param subject | |||
| * the subject, may be null | |||
| * @param template | |||
| * the FreeMarker template name, must not be null | |||
| * @param model | |||
| * the model to be applied to the FreeMarker template, may be null | |||
| * @param attachmentFilename | |||
| * the name of the attachment as it will appear in the mail | |||
| * @param file | |||
| * the File to attach | |||
| */ | |||
| public void sendFreeMarkerHtmlMail(int sysGroupId,InternetAddress from, InternetAddress replyTo, InternetAddress[] to, | |||
| String subject, String template, Map<?, ?> model, String attachmentFilename, File file) { | |||
| sendFreeMarkerHtmlMail(sysGroupId, from, replyTo, to, null, null, subject, template, model, attachmentFilename, file); | |||
| } | |||
| /** | |||
| * Send HTML mail message generated from FreeMarker template | |||
| * | |||
| * @param from | |||
| * the from address, must not be null | |||
| * @param replyTo | |||
| * the reply to address, may be null | |||
| * @param to | |||
| * the to addresses, must not be null | |||
| * @param subject | |||
| * the subject, may be null | |||
| * @param template | |||
| * the FreeMarker template name, must not be null | |||
| * @param model | |||
| * the model to be applied to the FreeMarker template, may be null | |||
| * @param attachmentFilename | |||
| * the name of the attachment as it will appear in the mail | |||
| * @param file | |||
| * the File to attach | |||
| */ | |||
| public void sendFreeMarkerHtmlMail(int sysGroupId, InternetAddress from, InternetAddress replyTo, InternetAddress[] to, | |||
| String subject, String template, Map<?, ?> model, String attachmentFilename, InputStreamSource file) { | |||
| String html = freeMarkerEngine.generateFreeMarkerContent(template, model); | |||
| MimeMessage mimeMessage = getJavaMailSender(sysGroupId).createMimeMessage(); | |||
| try { | |||
| MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8"); | |||
| message.setFrom(from); | |||
| if (replyTo != null) message.setReplyTo(replyTo); | |||
| message.setTo(to); | |||
| if (subject != null) message.setSubject(subject); | |||
| message.setText(html, true); | |||
| if (attachmentFilename != null && file != null) message.addAttachment(attachmentFilename, file); | |||
| } catch (MessagingException e) { | |||
| logger.error("Failed to send HTML mail", e); | |||
| } | |||
| getJavaMailSender(sysGroupId).send(mimeMessage); | |||
| } | |||
| /** | |||
| * Send HTML mail message generated from FreeMarker template | |||
| * | |||
| * @param from | |||
| * the from address, must not be null | |||
| * @param replyTo | |||
| * the reply to address, may be null | |||
| * @param to | |||
| * the to addresses, must not be null | |||
| * @param subject | |||
| * the subject, may be null | |||
| * @param template | |||
| * the FreeMarker template name, must not be null | |||
| * @param model | |||
| * the model to be applied to the FreeMarker template, may be null | |||
| */ | |||
| public void sendFreeMarkerHtmlMail(int sysGroupId, InternetAddress from, InternetAddress replyTo, InternetAddress[] to, | |||
| String subject, String template, Map<?, ?> model) { | |||
| sendFreeMarkerHtmlMail(sysGroupId,from, replyTo, to, null, null, subject, template, model, null, null); | |||
| } | |||
| } | |||
| @@ -0,0 +1,109 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.i18n.support; | |||
| import java.text.MessageFormat; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Locale; | |||
| import java.util.Map; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.context.support.AbstractMessageSource; | |||
| import com.ffii.core.dao.JdbcDao; | |||
| /** | |||
| * Jdbc MessageSource implementation. | |||
| * | |||
| * <pre> | |||
| * CREATE TABLE `i18n` ( | |||
| * `locale` varchar(10) NOT NULL, | |||
| * `code` varchar(100) NOT NULL, | |||
| * `value` varchar(500) DEFAULT NULL, | |||
| * PRIMARY KEY (`locale`,`code`) | |||
| * ); | |||
| * </pre> | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class JdbcMessageSource extends AbstractMessageSource { | |||
| @Autowired | |||
| private JdbcDao jdbcDao; | |||
| @Override | |||
| protected String resolveCodeWithoutArguments(String code, Locale locale) { | |||
| return getMessages(locale.toString()).get(code); | |||
| } | |||
| @Override | |||
| protected MessageFormat resolveCode(String code, Locale locale) { | |||
| String msg = getMessages(locale.toString()).get(code); | |||
| return createMessageFormat(msg, locale); | |||
| } | |||
| /** i18n messages cache */ | |||
| private static Map<String, Map<String, String>> MESSAGES = new HashMap<String, Map<String, String>>(); | |||
| /** | |||
| * Load a list of messages from i18n table in database | |||
| * | |||
| * @param locale | |||
| * the locale String (e.g. {@code en_US}) | |||
| */ | |||
| public List<Map<String, Object>> loadMessages(String locale) { | |||
| /* prep args */ | |||
| Map<String, Object> args = new HashMap<String, Object>(); | |||
| args.put("locale", locale); | |||
| /* build sql */ | |||
| String sql = "SELECT locale, code, value FROM i18n WHERE locale = :locale"; | |||
| /* do query and return */ | |||
| return jdbcDao.queryForList(sql, args); | |||
| } | |||
| /** | |||
| * Get i18n messages map | |||
| * | |||
| * @param locale | |||
| * the locale String (e.g. {@code en_US}) | |||
| */ | |||
| public Map<String, String> getMessages(String locale) { | |||
| Map<String, String> messages = MESSAGES.get(locale); | |||
| /* if not loaded yet, load it from database */ | |||
| if (messages == null) { | |||
| messages = new HashMap<String, String>(); | |||
| /* convert the list to a simple messages map */ | |||
| List<Map<String, Object>> rawMessages = loadMessages(locale); | |||
| for (Map<String, Object> rm : rawMessages) { | |||
| messages.put((String) rm.get("code"), (String) rm.get("value")); | |||
| } | |||
| /* cache the messages */ | |||
| MESSAGES.put(locale, messages); | |||
| } | |||
| return messages; | |||
| } | |||
| /** | |||
| * Reset the i18n messages cache | |||
| */ | |||
| public void resetMessagesCache() { | |||
| /* simply create a new map */ | |||
| MESSAGES = new HashMap<String, Map<String, String>>(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,52 @@ | |||
| package com.ffii.core.security.authentication; | |||
| import com.ffii.core.User; | |||
| import org.springframework.security.authentication.AbstractAuthenticationToken; | |||
| /** | |||
| * Authentication Token | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class AuthToken extends AbstractAuthenticationToken { | |||
| private static final long serialVersionUID = 3383254131623375507L; | |||
| private final String token; | |||
| private final User user; | |||
| public AuthToken(String token) { | |||
| super(null); | |||
| this.token = token; | |||
| this.user = null; | |||
| setAuthenticated(false); | |||
| } | |||
| public AuthToken(String token, User user) { | |||
| super(user.getAuthorities()); | |||
| this.token = token; | |||
| this.user = user; | |||
| setAuthenticated(true); | |||
| } | |||
| @Override | |||
| public Object getCredentials() { | |||
| return getToken(); | |||
| } | |||
| @Override | |||
| public Object getPrincipal() { | |||
| return getUser(); | |||
| } | |||
| public String getToken() { | |||
| return token; | |||
| } | |||
| public User getUser() { | |||
| return user; | |||
| } | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.security.authentication; | |||
| import com.ffii.core.User; | |||
| import com.ffii.core.security.service.TokenUserDetailsService; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.security.authentication.AuthenticationProvider; | |||
| import org.springframework.security.authentication.BadCredentialsException; | |||
| import org.springframework.security.core.Authentication; | |||
| import org.springframework.security.core.AuthenticationException; | |||
| import org.springframework.stereotype.Component; | |||
| /** | |||
| * Token Authentication Provider | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| @Component | |||
| public class TokenAuthenticationProvider implements AuthenticationProvider { | |||
| @Autowired | |||
| private TokenUserDetailsService tokenUserDetailsService; | |||
| @Override | |||
| public Authentication authenticate(Authentication authentication) throws AuthenticationException { | |||
| final AuthToken authToken = (AuthToken) authentication; | |||
| final String token = authToken.getToken(); | |||
| User user = tokenUserDetailsService.findUserByToken(token); | |||
| if (user == null) | |||
| throw new BadCredentialsException("No user found for token - " + token); | |||
| return new AuthToken(token, user); | |||
| } | |||
| @Override | |||
| public boolean supports(Class<?> authentication) { | |||
| return authentication.equals(AuthToken.class); | |||
| } | |||
| } | |||
| @@ -0,0 +1,74 @@ | |||
| package com.ffii.core.security.filter; | |||
| import java.io.IOException; | |||
| import java.util.Collections; | |||
| import javax.servlet.FilterChain; | |||
| import javax.servlet.ServletException; | |||
| import javax.servlet.ServletRequest; | |||
| import javax.servlet.ServletResponse; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import com.ffii.core.security.authentication.AuthToken; | |||
| import com.ffii.core.utils.StringUtils; | |||
| import org.springframework.security.core.Authentication; | |||
| import org.springframework.security.core.AuthenticationException; | |||
| import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; | |||
| import org.springframework.security.web.util.matcher.RequestMatcher; | |||
| /** | |||
| * Authentication Token Filter | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class AuthTokenFilter extends AbstractAuthenticationProcessingFilter { | |||
| public static final String TOKEN_HEADER = "x-auth-token"; | |||
| public AuthTokenFilter(RequestMatcher requestMatcher) { | |||
| super(requestMatcher); | |||
| } | |||
| @Override | |||
| public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | |||
| throws IOException, ServletException { | |||
| final String tokenValue = getTokenValue((HttpServletRequest) request); | |||
| // This filter only applies if the header is present | |||
| if (StringUtils.isEmpty(tokenValue)) { | |||
| chain.doFilter(request, response); | |||
| return; | |||
| } | |||
| // On success keep going on the chain | |||
| this.setAuthenticationSuccessHandler((request1, response1, authentication) -> { | |||
| chain.doFilter(request1, response1); | |||
| }); | |||
| super.doFilter(request, response, chain); | |||
| } | |||
| @Override | |||
| public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) | |||
| throws AuthenticationException, IOException, ServletException { | |||
| final String tokenValue = getTokenValue(request); | |||
| if (StringUtils.isEmpty(tokenValue)) { | |||
| return null; | |||
| } | |||
| AuthToken token = new AuthToken(tokenValue); | |||
| token.setDetails(authenticationDetailsSource.buildDetails(request)); | |||
| return this.getAuthenticationManager().authenticate(token); | |||
| } | |||
| private String getTokenValue(HttpServletRequest request) { | |||
| return Collections.list(request.getHeaderNames()).stream() | |||
| .filter(header -> TOKEN_HEADER.equalsIgnoreCase(header)).map(header -> request.getHeader(header)) | |||
| .findFirst().orElse(null); | |||
| } | |||
| } | |||
| @@ -0,0 +1,65 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.security.service; | |||
| import java.util.Date; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import com.ffii.core.dao.JdbcDao; | |||
| import com.ffii.core.utils.MapUtils; | |||
| import com.ffii.core.web.AbstractService; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Service; | |||
| import org.springframework.transaction.annotation.Isolation; | |||
| import org.springframework.transaction.annotation.Transactional; | |||
| /** | |||
| * Login Log Service | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| @Service | |||
| public class LoginLogService extends AbstractService { | |||
| @Autowired | |||
| private JdbcDao jdbcDao; | |||
| /** | |||
| * Create Login Log record | |||
| * | |||
| * @param username | |||
| * the username | |||
| * @param remoteAddr | |||
| * request.getRemoteAddr() | |||
| * @param success | |||
| * true if the login is successful, else false | |||
| * | |||
| * @return true if the number of rows inserted is 1 | |||
| */ | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = false) | |||
| public boolean createLoginLog(String username, String remoteAddr, boolean success) { | |||
| Map<String, Object> args = new HashMap<>(4); | |||
| args.put("username", username); | |||
| args.put("loginTime", new Date()); | |||
| args.put("ipAddr", remoteAddr); | |||
| args.put("success", success); | |||
| return (jdbcDao.executeInsert("user_login_log", args) == 1); | |||
| } | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| public List<Map<String, Object>> listLastLog(String username, int limit) { | |||
| return jdbcDao.queryForList("SELECT success FROM user_login_log where username = :username ORDER BY loginTime DESC LIMIT " + limit, | |||
| MapUtils.toHashMap("username", username)); | |||
| } | |||
| } | |||
| @@ -0,0 +1,143 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.security.service; | |||
| import java.sql.ResultSet; | |||
| import java.sql.SQLException; | |||
| import java.util.ArrayList; | |||
| import java.util.HashSet; | |||
| import java.util.List; | |||
| import java.util.Set; | |||
| import com.ffii.core.User; | |||
| import com.ffii.core.dao.JdbcDao; | |||
| import com.ffii.core.utils.MapUtils; | |||
| import com.ffii.core.web.AbstractService; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.context.support.MessageSourceAccessor; | |||
| import org.springframework.jdbc.core.RowMapper; | |||
| import org.springframework.security.core.GrantedAuthority; | |||
| import org.springframework.security.core.SpringSecurityMessageSource; | |||
| import org.springframework.security.core.authority.SimpleGrantedAuthority; | |||
| import org.springframework.security.core.userdetails.UserDetails; | |||
| import org.springframework.stereotype.Service; | |||
| import org.springframework.transaction.annotation.Isolation; | |||
| import org.springframework.transaction.annotation.Transactional; | |||
| /** | |||
| * Token User Details Service | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| @Service | |||
| public class TokenUserDetailsService extends AbstractService { | |||
| @Autowired | |||
| private JdbcDao jdbcDao; | |||
| public static final String FIND_USER_BY_DEVICE_TOKEN_SQL = "SELECT u.* FROM users u LEFT JOIN access_token at ON u.id = at.userId" | |||
| + " WHERE u.deleted = 0 AND at.token = :token"; | |||
| public static final String LOAD_AUTHORITIES_BY_USERNAME_SQL = "SELECT u.username, ua.authority" | |||
| + " FROM `users_authorities` ua" + " LEFT JOIN `users` u ON ua.userId = u.id" | |||
| + " WHERE u.deleted = 0 AND u.username = ?"; | |||
| public static final String LOAD_GROUP_AUTHORITIES_BY_USERNAME_SQL = "SELECT g.id, g.name, ga.authority" | |||
| + " FROM `groups_authorities` ga" + " LEFT JOIN `groups` g ON ga.groupId = g.id AND g.deleted = 0" | |||
| + " LEFT JOIN `groups_users` gu ON ga.groupId = gu.groupId" + " LEFT JOIN `users` u ON gu.userId = u.id" | |||
| + " WHERE u.deleted = 0 AND u.username = ?"; | |||
| protected final MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); | |||
| // ~ Constructors | |||
| public TokenUserDetailsService() { | |||
| } | |||
| // ~ Methods | |||
| /** | |||
| * Allows subclasses to add their own granted authorities to the list to be | |||
| * returned in the <tt>UserDetails</tt>. | |||
| * | |||
| * @param username the username, for use by finder methods | |||
| * @param authorities the current granted authorities, as populated from the | |||
| * <code>authoritiesByUsername</code> mapping | |||
| */ | |||
| protected void addCustomAuthorities(String username, List<GrantedAuthority> authorities) { | |||
| // add ROLE_USER for basic access | |||
| authorities.add(new SimpleGrantedAuthority("ROLE_USER")); | |||
| } | |||
| public UserDetails loadUserByToken(String token) { | |||
| User user = findUserByToken(token); // contains no GrantedAuthority[] | |||
| // build GrantedAuthority[] | |||
| Set<GrantedAuthority> authoritiesSet = new HashSet<>(); | |||
| // aadd all user's authorities | |||
| authoritiesSet.addAll(loadUserAuthorities(user.getUsername())); | |||
| // add all user's groups' authorities | |||
| authoritiesSet.addAll(loadGroupAuthorities(user.getUsername())); | |||
| // convert to List | |||
| List<GrantedAuthority> authoritiesList = new ArrayList<GrantedAuthority>(authoritiesSet); | |||
| addCustomAuthorities(user.getUsername(), authoritiesList); | |||
| user.setAuthorities(authoritiesList); | |||
| return user; | |||
| } | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| public User findUserByToken(String token) { | |||
| List<User> users = jdbcDao.query(FIND_USER_BY_DEVICE_TOKEN_SQL, User.class, MapUtils.toHashMap("token", token), | |||
| 1); | |||
| if (users.size() == 1) | |||
| return users.get(0); | |||
| else | |||
| return null; | |||
| } | |||
| /** | |||
| * Loads user authorities by executing SQL | |||
| * | |||
| * @return a list of GrantedAuthority objects for the user | |||
| */ | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| protected List<GrantedAuthority> loadUserAuthorities(String username) { | |||
| return jdbcDao.getNamedParameterJdbcTemplate().query(LOAD_AUTHORITIES_BY_USERNAME_SQL, | |||
| MapUtils.toHashMap("username", username), new RowMapper<GrantedAuthority>() { | |||
| @Override | |||
| public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { | |||
| return new SimpleGrantedAuthority(rs.getString("authority")); | |||
| } | |||
| }); | |||
| } | |||
| /** | |||
| * Loads group authorities by executing SQL | |||
| * | |||
| * @return a list of GrantedAuthority objects from the user's groups | |||
| */ | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| protected List<GrantedAuthority> loadGroupAuthorities(String username) { | |||
| return jdbcDao.getNamedParameterJdbcTemplate().query(LOAD_GROUP_AUTHORITIES_BY_USERNAME_SQL, | |||
| MapUtils.toHashMap("username", username), new RowMapper<GrantedAuthority>() { | |||
| @Override | |||
| public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { | |||
| return new SimpleGrantedAuthority(rs.getString("authority")); | |||
| } | |||
| }); | |||
| } | |||
| } | |||
| @@ -0,0 +1,239 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.security.userdetails; | |||
| import java.sql.ResultSet; | |||
| import java.sql.SQLException; | |||
| import java.util.ArrayList; | |||
| import java.util.HashSet; | |||
| import java.util.List; | |||
| import java.util.Set; | |||
| import com.ffii.core.User; | |||
| import org.springframework.context.ApplicationContextException; | |||
| import org.springframework.context.support.MessageSourceAccessor; | |||
| import org.springframework.jdbc.core.BeanPropertyRowMapper; | |||
| import org.springframework.jdbc.core.RowMapper; | |||
| import org.springframework.jdbc.core.support.JdbcDaoSupport; | |||
| import org.springframework.security.core.GrantedAuthority; | |||
| import org.springframework.security.core.SpringSecurityMessageSource; | |||
| import org.springframework.security.core.authority.SimpleGrantedAuthority; | |||
| import org.springframework.security.core.userdetails.UserDetails; | |||
| import org.springframework.security.core.userdetails.UserDetailsService; | |||
| import org.springframework.security.core.userdetails.UsernameNotFoundException; | |||
| import org.springframework.util.Assert; | |||
| public class UserDetailsServiceImpl extends JdbcDaoSupport implements UserDetailsService { | |||
| // ~ Static fields/initializers ===================================================================================== | |||
| public static final String DEF_USERS_BY_USERNAME_QUERY = "SELECT *" | |||
| + " FROM `users` WHERE deleted = 0 AND username = ?"; | |||
| public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = "SELECT u.username, ua.authority" | |||
| + " FROM `users_authorities` ua" | |||
| + " LEFT JOIN `users` u ON ua.userId = u.id" | |||
| + " WHERE u.deleted = 0 AND u.username = ?"; | |||
| public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY = "SELECT g.id, g.name, ga.authority" | |||
| + " FROM `groups_authorities` ga" | |||
| + " LEFT JOIN `groups` g ON ga.groupId = g.id AND g.deleted = 0" | |||
| + " LEFT JOIN `groups_users` gu ON ga.groupId = gu.groupId" | |||
| + " LEFT JOIN `users` u ON gu.userId = u.id" | |||
| + " WHERE u.deleted = 0 AND u.username = ?"; | |||
| // ~ Instance fields ================================================================================================ | |||
| protected final MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); | |||
| private String usersByUsernameQuery; | |||
| private String authoritiesByUsernameQuery; | |||
| private String groupAuthoritiesByUsernameQuery; | |||
| private boolean enableAuthorities = true; | |||
| private boolean enableGroups; | |||
| // ~ Constructors =================================================================================================== | |||
| public UserDetailsServiceImpl() { | |||
| usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY; | |||
| authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY; | |||
| groupAuthoritiesByUsernameQuery = DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY; | |||
| } | |||
| // ~ Methods ======================================================================================================== | |||
| /** | |||
| * Allows subclasses to add their own granted authorities to the list to be returned in the <tt>UserDetails</tt>. | |||
| * | |||
| * @param username | |||
| * the username, for use by finder methods | |||
| * @param authorities | |||
| * the current granted authorities, as populated from the <code>authoritiesByUsername</code> mapping | |||
| */ | |||
| protected void addCustomAuthorities(String username, List<GrantedAuthority> authorities) { | |||
| // add ROLE_USER for basic access | |||
| authorities.add(new SimpleGrantedAuthority("ROLE_USER")); | |||
| } | |||
| public String getUsersByUsernameQuery() { | |||
| return usersByUsernameQuery; | |||
| } | |||
| @Override | |||
| protected void initDao() throws ApplicationContextException { | |||
| Assert.isTrue(enableAuthorities || enableGroups, "Use of either authorities or groups must be enabled"); | |||
| } | |||
| @Override | |||
| public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { | |||
| List<User> users = loadUsersByUsername(username); | |||
| if (users.size() == 0) { | |||
| logger.debug("Query returned no results for user '" + username + "'"); | |||
| throw new UsernameNotFoundException( | |||
| messages.getMessage("JdbcDaoImpl.notFound", new Object[] { username }, "Username {0} not found")); | |||
| } | |||
| User user = users.get(0); // contains no GrantedAuthority[] | |||
| Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>(); | |||
| if (enableAuthorities) { | |||
| dbAuthsSet.addAll(loadUserAuthorities(user.getUsername())); | |||
| } | |||
| if (enableGroups) { | |||
| dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername())); | |||
| } | |||
| List<GrantedAuthority> dbAuths = new ArrayList<GrantedAuthority>(dbAuthsSet); | |||
| addCustomAuthorities(user.getUsername(), dbAuths); | |||
| if (dbAuths.size() == 0) { | |||
| logger.debug("User '" + username + "' has no authorities and will be treated as 'not found'"); | |||
| throw new UsernameNotFoundException( | |||
| messages.getMessage("USER.noAuthority", new Object[] { username }, "User {0} has no GrantedAuthority")); | |||
| } | |||
| user.setAuthorities(dbAuths); | |||
| return user; | |||
| } | |||
| /** | |||
| * Executes the SQL <tt>usersByUsernameQuery</tt> and returns a list of UserDetails objects. There should normally only be one matching user. | |||
| */ | |||
| protected List<User> loadUsersByUsername(String username) { | |||
| return getJdbcTemplate().query(usersByUsernameQuery, new String[] { username }, BeanPropertyRowMapper.newInstance(User.class)); | |||
| } | |||
| /** | |||
| * Loads authorities by executing the SQL from <tt>authoritiesByUsernameQuery</tt>. | |||
| * | |||
| * @return a list of GrantedAuthority objects for the user | |||
| */ | |||
| protected List<GrantedAuthority> loadUserAuthorities(String username) { | |||
| return getJdbcTemplate().query(authoritiesByUsernameQuery, new String[] { username }, new RowMapper<GrantedAuthority>() { | |||
| @Override | |||
| public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { | |||
| return new SimpleGrantedAuthority(rs.getString("authority")); | |||
| } | |||
| }); | |||
| } | |||
| /** | |||
| * Loads authorities by executing the SQL from <tt>groupAuthoritiesByUsernameQuery</tt>. | |||
| * | |||
| * @return a list of GrantedAuthority objects for the user | |||
| */ | |||
| protected List<GrantedAuthority> loadGroupAuthorities(String username) { | |||
| return getJdbcTemplate().query(groupAuthoritiesByUsernameQuery, new String[] { username }, new RowMapper<GrantedAuthority>() { | |||
| @Override | |||
| public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { | |||
| return new SimpleGrantedAuthority(rs.getString("authority")); | |||
| } | |||
| }); | |||
| } | |||
| /** | |||
| * Allows the default query string used to retrieve authorities based on username to be overridden, if default table or column names need to be changed. The | |||
| * default query is {@link #DEF_AUTHORITIES_BY_USERNAME_QUERY}; when modifying this query, ensure that all returned columns are mapped back to the same | |||
| * column names as in the default query. | |||
| * | |||
| * @param queryString | |||
| * The SQL query string to set | |||
| */ | |||
| public void setAuthoritiesByUsernameQuery(String queryString) { | |||
| authoritiesByUsernameQuery = queryString; | |||
| } | |||
| protected String getAuthoritiesByUsernameQuery() { | |||
| return authoritiesByUsernameQuery; | |||
| } | |||
| /** | |||
| * Allows the default query string used to retrieve group authorities based on username to be overridden, if default table or column names need to be | |||
| * changed. The default query is {@link #DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY}; when modifying this query, ensure that all returned columns are mapped | |||
| * back to the same column names as in the default query. | |||
| * | |||
| * @param queryString | |||
| * The SQL query string to set | |||
| */ | |||
| public void setGroupAuthoritiesByUsernameQuery(String queryString) { | |||
| groupAuthoritiesByUsernameQuery = queryString; | |||
| } | |||
| /** | |||
| * Allows the default query string used to retrieve users based on username to be overridden, if default table or column names need to be changed. The | |||
| * default query is {@link #DEF_USERS_BY_USERNAME_QUERY}; when modifying this query, ensure that all returned columns are mapped back to the same column | |||
| * names as in the default query. If the 'enabled' column does not exist in the source database, a permanent true value for this column may be returned by | |||
| * using a query similar to | |||
| * | |||
| * <pre> | |||
| * "select username,password,'true' as enabled from users where username = ?" | |||
| * </pre> | |||
| * | |||
| * @param usersByUsernameQueryString | |||
| * The query string to set | |||
| */ | |||
| public void setUsersByUsernameQuery(String usersByUsernameQueryString) { | |||
| this.usersByUsernameQuery = usersByUsernameQueryString; | |||
| } | |||
| protected boolean getEnableAuthorities() { | |||
| return enableAuthorities; | |||
| } | |||
| /** | |||
| * Enables loading of authorities from the authorities table. Defaults to true | |||
| */ | |||
| public void setEnableAuthorities(boolean enableAuthorities) { | |||
| this.enableAuthorities = enableAuthorities; | |||
| } | |||
| protected boolean getEnableGroups() { | |||
| return enableGroups; | |||
| } | |||
| /** | |||
| * Enables support for group authorities. Defaults to false | |||
| * | |||
| * @param enableGroups | |||
| */ | |||
| public void setEnableGroups(boolean enableGroups) { | |||
| this.enableGroups = enableGroups; | |||
| } | |||
| } | |||
| @@ -0,0 +1,100 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.security.web.authentication; | |||
| import java.io.IOException; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import javax.servlet.ServletException; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import org.apache.commons.logging.Log; | |||
| import org.apache.commons.logging.LogFactory; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.security.core.AuthenticationException; | |||
| import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; | |||
| import com.ffii.core.utils.BooleanUtils; | |||
| import com.ffii.core.utils.JsonUtils; | |||
| import com.ffii.core.utils.MapUtils; | |||
| import com.ffii.core.utils.Params; | |||
| import com.ffii.core.utils.StringUtils; | |||
| import com.ffii.core.utils.web.ServletRequestUtils; | |||
| import com.ffii.core.utils.web.ServletResponseUtils; | |||
| import com.ffii.core.web.view.AbstractView; | |||
| import com.ffii.core.security.service.LoginLogService; | |||
| import com.ffii.tbms.user.service.UserService; | |||
| /** | |||
| * <tt>AuthenticationFailureHandler</tt> implementation which return a failure JSON String upon failed authentication. | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class JsonAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { | |||
| /** Continuous fail 5 time, lock user */ | |||
| private final int TIMES = 5; | |||
| protected final Log logger = LogFactory.getLog(getClass()); | |||
| @Autowired | |||
| private UserService userService; | |||
| @Autowired | |||
| private LoginLogService loginLogService; | |||
| /** | |||
| * Returns the failure JSON String to client. | |||
| */ | |||
| @Override | |||
| public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) | |||
| throws IOException, ServletException { | |||
| logger.info("Authentication Failure: " + exception.getMessage() + " [" + request.getRemoteAddr() + "]"); | |||
| String username = StringUtils.left(ServletRequestUtils.getStringParameter(request, "username"), 32); | |||
| ServletResponseUtils.disableCaching(response); | |||
| response.setContentType(AbstractView.CONTENT_TYPE_JSON); | |||
| response.setCharacterEncoding("UTF-8"); | |||
| response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // set HTTP status to 401 | |||
| boolean locked = false; | |||
| // log failed login | |||
| if (username != null) { | |||
| loginLogService.createLoginLog(username, request.getRemoteAddr(), false); | |||
| // when failed 5 times, lock account | |||
| List<Map<String, Object>> logs = loginLogService.listLastLog(username, TIMES); | |||
| if (logs.size() >= TIMES) { | |||
| boolean needLock = true; | |||
| for (Map<String, Object> log : logs) { | |||
| if (BooleanUtils.isTrue(log.get("success"))) { | |||
| needLock = false; | |||
| break; | |||
| } | |||
| } | |||
| if (needLock) { | |||
| locked = true; | |||
| userService.lockUser(username, true); | |||
| } | |||
| } | |||
| } | |||
| Map<String, Object> result = MapUtils.toHashMap( | |||
| Params.SUCCESS, Boolean.FALSE, | |||
| Params.MSG, | |||
| locked ? "Account locked (" + TIMES + " Times Failure), please contact your IT administrator." : "Invalid Username or Password."); | |||
| ServletResponseUtils.writeStringToStream(response, AbstractView.CHARSET_UTF8, JsonUtils.toJsonString(result)); | |||
| } | |||
| } | |||
| @@ -0,0 +1,119 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.security.web.authentication; | |||
| import java.io.IOException; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.net.URLEncoder; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import javax.servlet.ServletException; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import com.ffii.core.Session; | |||
| import com.ffii.core.Settings; | |||
| import com.ffii.core.security.service.LoginLogService; | |||
| import com.ffii.core.setting.service.SettingsService; | |||
| import com.ffii.core.utils.JsonUtils; | |||
| import com.ffii.core.utils.LocaleUtils; | |||
| import com.ffii.core.utils.Params; | |||
| import com.ffii.core.utils.SecurityUtils; | |||
| import com.ffii.core.utils.web.ServletResponseUtils; | |||
| import com.ffii.core.web.view.AbstractView; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.http.MediaType; | |||
| import org.springframework.security.core.Authentication; | |||
| import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; | |||
| import org.springframework.security.web.savedrequest.HttpSessionRequestCache; | |||
| import org.springframework.security.web.savedrequest.RequestCache; | |||
| import org.springframework.security.web.savedrequest.SavedRequest; | |||
| import org.springframework.util.StringUtils; | |||
| import org.springframework.web.servlet.i18n.SessionLocaleResolver; | |||
| import org.springframework.web.util.WebUtils; | |||
| /** | |||
| * <tt>AuthenticationSuccessHandler</tt> implementation which return a success JSON String upon successful authentication. | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class JsonAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { | |||
| private RequestCache requestCache = new HttpSessionRequestCache(); | |||
| @Autowired | |||
| private LoginLogService loginLogService; | |||
| @Autowired | |||
| private SettingsService settingsService; | |||
| /** | |||
| * Returns the successful JSON String to client, unless the account is locked. | |||
| */ | |||
| @Override | |||
| public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) | |||
| throws IOException, ServletException { | |||
| logger.info("Authentication Success: " + authentication.getName() + " [" + request.getRemoteAddr() + "]"); | |||
| ServletResponseUtils.disableCaching(response); | |||
| response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); | |||
| response.setCharacterEncoding("UTF-8"); | |||
| // save available locales to session | |||
| String[] availableLocales = settingsService.getString(Settings.SYS_AVAILABLE_LOCALES).split(","); | |||
| Session.setAttribute(request, Session.AVAILABLE_LOCALES, availableLocales); | |||
| // set user's default locale | |||
| String locale = SecurityUtils.getUser().getLocale(); | |||
| if (!StringUtils.isEmpty(locale)) | |||
| WebUtils.setSessionAttribute(request, SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, LocaleUtils.toLocale(locale)); | |||
| // extra values to be passed to client | |||
| Map<String, Object> values = new HashMap<String, Object>(); | |||
| SavedRequest savedRequest = requestCache.getRequest(request, response); | |||
| if (savedRequest != null) { | |||
| String targetUrlParameter = getTargetUrlParameter(); | |||
| if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) { | |||
| requestCache.removeRequest(request, response); | |||
| authenticationSuccess(request, response, null); | |||
| return; | |||
| } | |||
| // Use the DefaultSavedRequest URL | |||
| String targetUrl = savedRequest.getRedirectUrl(); | |||
| logger.debug("Sending DefaultSavedRequest Url: " + targetUrl); | |||
| // target URL to redirect to (if any) | |||
| values.put("targetUrl", URLEncoder.encode(targetUrl, "UTF-8")); | |||
| } | |||
| authenticationSuccess(request, response, values); | |||
| } | |||
| protected void authenticationSuccess(HttpServletRequest request, HttpServletResponse response, Map<String, Object> values) | |||
| throws UnsupportedEncodingException, IOException { | |||
| Map<String, Object> args = new HashMap<String, Object>(); | |||
| args.put(Params.SUCCESS, Boolean.TRUE); | |||
| if (values != null) args.putAll(values); | |||
| clearAuthenticationAttributes(request); | |||
| ServletResponseUtils.writeStringToStream(response, AbstractView.CHARSET_UTF8, JsonUtils.toJsonString(args)); | |||
| // log successful login | |||
| loginLogService.createLoginLog(SecurityUtils.getUser().getUsername(), request.getRemoteAddr(), true); | |||
| } | |||
| } | |||
| @@ -0,0 +1,64 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.setting; | |||
| import java.io.Serializable; | |||
| public class Setting implements Serializable { | |||
| private static final long serialVersionUID = 3925955875238868120L; | |||
| private String category; | |||
| private String type; | |||
| private String name; | |||
| private String value; | |||
| /** default constructor */ | |||
| public Setting() { | |||
| } | |||
| public String getCategory() { | |||
| return category; | |||
| } | |||
| public void setCategory(String category) { | |||
| this.category = category; | |||
| } | |||
| public String getType() { | |||
| return type; | |||
| } | |||
| public void setType(String type) { | |||
| this.type = type; | |||
| } | |||
| public String getName() { | |||
| return name; | |||
| } | |||
| public void setName(String name) { | |||
| this.name = name; | |||
| } | |||
| public String getValue() { | |||
| return value; | |||
| } | |||
| public void setValue(String value) { | |||
| this.value = value; | |||
| } | |||
| } | |||
| @@ -0,0 +1,169 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.setting.service; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Service; | |||
| import org.springframework.transaction.annotation.Isolation; | |||
| import org.springframework.transaction.annotation.Transactional; | |||
| import com.ffii.core.dao.JdbcDao; | |||
| import com.ffii.core.setting.Setting; | |||
| import com.ffii.core.utils.NumberUtils; | |||
| import com.ffii.core.utils.Params; | |||
| /** | |||
| * System Settings Service | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| @Service | |||
| public class SettingsService { | |||
| private static final String SQL_LOAD_SETTINGS = "SELECT * FROM settings ORDER BY category, name"; | |||
| private static final String SQL_INSERT_OR_UPDATE_SETTING = "INSERT INTO settings (`name`, `value`) VALUES (:name, :value) ON DUPLICATE KEY UPDATE `name` = VALUES(`name`), `value` = VALUES(`value`)"; | |||
| /** | |||
| * Settings cache | |||
| */ | |||
| private static Map<String, Setting> SETTINGS = null; | |||
| @Autowired | |||
| private JdbcDao jdbcDao; | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| public List<Map<String, Object>> searchSettings(Map<String, Object> args) { | |||
| StringBuilder sql = new StringBuilder("SELECT * FROM settings WHERE 1=1"); | |||
| if (args.containsKey("category")) sql.append(" AND category = :category"); | |||
| if (args.containsKey("type")) sql.append(" AND type = :type"); | |||
| if (args.containsKey("name")) sql.append(" AND name = :name"); | |||
| sql.append(" ORDER BY category, name"); | |||
| return jdbcDao.queryForList(sql.toString(), args); | |||
| } | |||
| /** | |||
| * Load and return a Map of all system settings from database | |||
| */ | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| public Map<String, Setting> loadSettingsMap() { | |||
| List<Setting> settings = jdbcDao.query(SQL_LOAD_SETTINGS, Setting.class, null, 0); | |||
| Map<String, Setting> settingsMap = new HashMap<String, Setting>(); | |||
| for (Setting setting : settings) { | |||
| settingsMap.put(setting.getName(), setting); | |||
| } | |||
| return settingsMap; | |||
| } | |||
| /** | |||
| * @param name | |||
| * the name of the setting | |||
| * @param defaultValue | |||
| * the default value to return if setting is not found | |||
| * | |||
| * @return the String value of the setting, or the default value if not found | |||
| */ | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| public String getString(String name, String defaultValue) { | |||
| if (SETTINGS == null) SETTINGS = loadSettingsMap(); | |||
| return SETTINGS.get(name) != null ? SETTINGS.get(name).getValue() : defaultValue; | |||
| } | |||
| /** | |||
| * @param name | |||
| * the name of the setting | |||
| * | |||
| * @return the String value of the setting, or {@code null} if not found | |||
| */ | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| public String getString(String name) { | |||
| return getString(name, null); | |||
| } | |||
| /** | |||
| * @param name | |||
| * the name of the setting | |||
| * @param defaultValue | |||
| * the default value to return if setting is not found | |||
| * | |||
| * @return the int value of the setting, or the default value if not found | |||
| */ | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| public int getInt(String name, int defaultValue) { | |||
| return NumberUtils.toInt(getString(name), defaultValue); | |||
| } | |||
| /** | |||
| * @param name | |||
| * the name of the setting | |||
| * | |||
| * @return the int value of the setting, or zero (0) if not found | |||
| */ | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| public int getInt(String name) { | |||
| return getInt(name, 0); | |||
| } | |||
| /** | |||
| * @param name | |||
| * the name of the setting | |||
| * @param defaultValue | |||
| * the default value to return if setting is not found | |||
| * | |||
| * @return the double value of the setting, or the default value if not found | |||
| */ | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| public double getDouble(String name, double defaultValue) { | |||
| return NumberUtils.toDouble(getString(name), defaultValue); | |||
| } | |||
| /** | |||
| * @param name | |||
| * the name of the setting | |||
| * | |||
| * @return the double value of the setting, or zero (0.0) if not found | |||
| */ | |||
| @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||
| public double getDouble(String name) { | |||
| return getDouble(name, 0.0d); | |||
| } | |||
| /** | |||
| * @param name | |||
| * the name of the setting | |||
| * @param value | |||
| * the value of the setting | |||
| */ | |||
| @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false) | |||
| public int saveSetting(String name, String value) { | |||
| Map<String, Object> args = new HashMap<String, Object>(2); | |||
| args.put(Params.NAME, name); | |||
| args.put(Params.VALUE, value); | |||
| return jdbcDao.executeUpdate(SQL_INSERT_OR_UPDATE_SETTING, args); | |||
| } | |||
| /** | |||
| * Reset the settings cache | |||
| */ | |||
| public void resetSettingsCache() { | |||
| // simply set the settings cache to null | |||
| SETTINGS = null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.support; | |||
| import java.beans.PropertyEditorSupport; | |||
| import java.util.Date; | |||
| import org.springframework.util.StringUtils; | |||
| import com.ffii.core.utils.DateUtils; | |||
| /** | |||
| * Property Editor for Date in the format defined by {@link DateUtils#PARSE_PATTERNS}<br> | |||
| * <p> | |||
| * Defaults to <code>null</code> if cannot be parsed, never throw an Exception | |||
| * </p> | |||
| * | |||
| * @see DateUtils#parseDateStrictly(String, String[], Date) | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class SimpleDateEditor extends PropertyEditorSupport { | |||
| @Override | |||
| public void setAsText(String text) throws IllegalArgumentException { | |||
| if (StringUtils.hasText(text)) | |||
| setValue(DateUtils.parseDateStrictly(text, DateUtils.PARSE_PATTERNS, null)); | |||
| else | |||
| setValue(null); | |||
| } | |||
| @Override | |||
| public String getAsText() { | |||
| return DateUtils.formatDate((Date) getValue()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.support; | |||
| import java.beans.PropertyEditorSupport; | |||
| import java.math.BigDecimal; | |||
| import com.ffii.core.utils.NumberUtils; | |||
| import com.ffii.core.utils.StringUtils; | |||
| /** | |||
| * Property Editor for BigDecimal (Defaults to <code>null</code> if cannot be parsed, never throw an Exception) | |||
| * | |||
| * @see NumberUtils#toDecimal(String, BigDecimal) | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class SimpleDecimalEditor extends PropertyEditorSupport { | |||
| @Override | |||
| public void setAsText(String value) throws IllegalArgumentException { | |||
| setValue(NumberUtils.toDecimal(value, null)); | |||
| } | |||
| @Override | |||
| public String getAsText() { | |||
| return (getValue() == null ? StringUtils.EMPTY : getValue().toString()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.support; | |||
| import java.beans.PropertyEditorSupport; | |||
| import com.ffii.core.utils.NumberUtils; | |||
| import com.ffii.core.utils.StringUtils; | |||
| /** | |||
| * Property Editor for Double (Defaults to <code>null</code> if cannot be parsed, never throw an Exception) | |||
| * | |||
| * @see NumberUtils#toDouble(String, Double) | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class SimpleDoubleEditor extends PropertyEditorSupport { | |||
| @Override | |||
| public void setAsText(String value) throws IllegalArgumentException { | |||
| setValue(NumberUtils.toDouble(value, null)); | |||
| } | |||
| @Override | |||
| public String getAsText() { | |||
| return (getValue() == null ? StringUtils.EMPTY : getValue().toString()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.support; | |||
| import java.beans.PropertyEditorSupport; | |||
| import com.ffii.core.utils.NumberUtils; | |||
| import com.ffii.core.utils.StringUtils; | |||
| /** | |||
| * Property Editor for Integer (Defaults to <code>null</code> if cannot be parsed, never throw an Exception) | |||
| * | |||
| * @see NumberUtils#toInt(String, Integer) | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class SimpleIntegerEditor extends PropertyEditorSupport { | |||
| @Override | |||
| public void setAsText(String value) throws IllegalArgumentException { | |||
| setValue(NumberUtils.toInt(value, null)); | |||
| } | |||
| @Override | |||
| public String getAsText() { | |||
| return (getValue() == null ? StringUtils.EMPTY : getValue().toString()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.support; | |||
| import java.beans.PropertyEditorSupport; | |||
| import com.ffii.core.utils.NumberUtils; | |||
| import com.ffii.core.utils.StringUtils; | |||
| /** | |||
| * Property Editor for Long (Defaults to <code>null</code> if cannot be parsed, never throw an Exception) | |||
| * | |||
| * @see NumberUtils#toLong(String, Long) | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class SimpleLongEditor extends PropertyEditorSupport { | |||
| @Override | |||
| public void setAsText(String value) throws IllegalArgumentException { | |||
| setValue(NumberUtils.toLong(value, null)); | |||
| } | |||
| @Override | |||
| public String getAsText() { | |||
| return (getValue() == null ? StringUtils.EMPTY : getValue().toString()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.support; | |||
| import java.beans.PropertyEditorSupport; | |||
| import com.ffii.core.utils.StringUtils; | |||
| /** | |||
| * <p> | |||
| * Property Editor for String | |||
| * </p> | |||
| * <p> | |||
| * Use {@link StringUtils.trimToEmpty()} when binding the value and getting the value to display | |||
| * </p> | |||
| * | |||
| * @see StringUtils#trimToEmpty(String) | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class SimpleStringTrimToEmptyEditor extends PropertyEditorSupport { | |||
| @Override | |||
| public void setAsText(String value) throws IllegalArgumentException { | |||
| setValue(StringUtils.trimToEmpty(value)); | |||
| } | |||
| @Override | |||
| public String getAsText() { | |||
| return (getValue() == null ? StringUtils.EMPTY : StringUtils.trimToEmpty((String) getValue())); | |||
| } | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.support; | |||
| import java.beans.PropertyEditorSupport; | |||
| import com.ffii.core.utils.StringUtils; | |||
| /** | |||
| * <p> | |||
| * Property Editor for String | |||
| * </p> | |||
| * <p> | |||
| * Use {@link StringUtils.trimToNull()} when binding the value, but use {@link StringUtils.trimToEmpty()} when getting the value to display | |||
| * </p> | |||
| * | |||
| * @see StringUtils#trimToNull(String) | |||
| * @see StringUtils#trimToEmpty(String) | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class SimpleStringTrimToNullEditor extends PropertyEditorSupport { | |||
| @Override | |||
| public void setAsText(String value) throws IllegalArgumentException { | |||
| setValue(StringUtils.trimToNull(value)); | |||
| } | |||
| @Override | |||
| public String getAsText() { | |||
| return (getValue() == null ? StringUtils.EMPTY : StringUtils.trimToEmpty((String) getValue())); | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| package com.ffii.core.utils; | |||
| import java.util.Map; | |||
| /** | |||
| * @author fung | |||
| */ | |||
| public class ArgsBuilder extends MapBuilder<String, Object> { | |||
| public ArgsBuilder() { | |||
| } | |||
| public ArgsBuilder(Map<String, Object> args) { | |||
| super(args); | |||
| } | |||
| } | |||
| @@ -0,0 +1,67 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| public class BooleanUtils extends org.apache.commons.lang3.BooleanUtils { | |||
| /** | |||
| * <p> | |||
| * For the case of {@code Number}, only if the value is {@code 1} will return {@code true}. Otherwise, {@code false} is returned. | |||
| * </p> | |||
| * <p> | |||
| * For the case of {@code String}, {@code 'true'}, {@code 'on'} or {@code 'yes'} (case insensitive) will return {@code true}. Otherwise, {@code false} is | |||
| * returned. | |||
| * </p> | |||
| * <p> | |||
| * For the case of {@code Boolean}, only if the {@code Boolean} value is {@code true} will return {@code true}, handling {@code null} by returning | |||
| * {@code false}. | |||
| * </p> | |||
| * <p> | |||
| * For any other cases including {@code null} will return {@code false}. | |||
| * </p> | |||
| */ | |||
| public static boolean isTrue(Object obj) { | |||
| if (obj instanceof Number) { | |||
| return ((Number) obj).intValue() == 1; | |||
| } else if (obj instanceof String) { | |||
| return toBoolean((String) obj); | |||
| } else { | |||
| return Boolean.TRUE.equals(obj); | |||
| } | |||
| } | |||
| /** | |||
| * <p> | |||
| * For the case of {@code Number}, only if the value is {@code 0} will return {@code true}. Otherwise, {@code false} is returned. | |||
| * </p> | |||
| * <p> | |||
| * For the case of {@code String}, {@code true} is returned unless the value is {@code 'true'}, {@code 'on'} or {@code 'yes'} (case insensitive) which will | |||
| * return {@code false}. | |||
| * </p> | |||
| * <p> | |||
| * For the case of {@code Boolean}, only if the {@code Boolean} value is {@code false} will return {@code true}, handling {@code null} by returning | |||
| * {@code false}. | |||
| * </p> | |||
| * <p> | |||
| * For any other cases including {@code null} will return {@code false}. | |||
| * </p> | |||
| */ | |||
| public static boolean isFalse(Object obj) { | |||
| if (obj instanceof Number) { | |||
| return ((Number) obj).intValue() == 0; | |||
| } else if (obj instanceof String) { | |||
| return !toBoolean((String) obj); | |||
| } else { | |||
| return Boolean.FALSE.equals(obj); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,66 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| /** | |||
| * CheckDigitUtils | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class CheckDigitUtils { | |||
| /* | |||
| * ISO/IEC 7812-1 Annex B | |||
| * Check digit calc method | |||
| */ | |||
| public static int luhnCalc(String digitsString) { | |||
| int sum = 0; | |||
| boolean alternate = false; | |||
| for (int i = 0; i < digitsString.length(); i++) { | |||
| int n = Integer.parseInt(digitsString.substring(i, i + 1)); | |||
| if (alternate) { | |||
| n *= 2; | |||
| if (n > 9) { | |||
| n = (n % 10) + 1; | |||
| } | |||
| } | |||
| sum += n; | |||
| alternate = !alternate; | |||
| } | |||
| return ((sum * 9) % 10); | |||
| } | |||
| /* | |||
| * ISO/IEC 7812-1 Annex B | |||
| * Check digit check method | |||
| */ | |||
| public static boolean luhnCheck(String digitsStringWithCheckDigit) { | |||
| int sum = 0; | |||
| boolean alternate = false; | |||
| for (int i = digitsStringWithCheckDigit.length() - 1; i >= 0; i--) { | |||
| try { | |||
| int n = Integer.parseInt(digitsStringWithCheckDigit.substring(i, i + 1)); | |||
| if (alternate) { | |||
| n *= 2; | |||
| if (n > 9) { | |||
| n = (n % 10) + 1; | |||
| } | |||
| } | |||
| sum += n; | |||
| alternate = !alternate; | |||
| } catch (NumberFormatException nfe) { | |||
| continue; | |||
| } | |||
| } | |||
| return (sum % 10 == 0); | |||
| } | |||
| } | |||
| @@ -0,0 +1,119 @@ | |||
| package com.ffii.core.utils; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import org.springframework.web.bind.ServletRequestBindingException; | |||
| /** | |||
| * ArgsBuilder of Criteria | |||
| * | |||
| * @see {@link CriteriaUtils} | |||
| * @author fung | |||
| */ | |||
| public class CriteriaArgsBuilder { | |||
| private Map<String, Object> args; | |||
| private HttpServletRequest request; | |||
| public CriteriaArgsBuilder(HttpServletRequest request) { | |||
| args = new HashMap<String, Object>(); | |||
| this.request = request; | |||
| } | |||
| public CriteriaArgsBuilder(HttpServletRequest request, Map<String, Object> args) { | |||
| this.args = args; | |||
| this.request = request; | |||
| } | |||
| public CriteriaArgsBuilder addStringExact(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addStringExact(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addStringLike(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addStringLike(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addString(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addString(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addStringContains(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addStringContains(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addStringStartsWith(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addStringStartsWith(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addStringEndsWith(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addStringEndsWith(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addStringList(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addStringList(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addStringCsv(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addStringCsv(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addInteger(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addInteger(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addNonZeroInteger(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addNonZeroInteger(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addIntegerList(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addIntegerList(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addNonZeroIntegerList(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addNonZeroIntegerList(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addLong(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addLong(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addNonZeroLong(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addNonZeroLong(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addDate(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addDate(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addDateTo(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addDateTo(request, args, paramName); | |||
| return this; | |||
| } | |||
| public CriteriaArgsBuilder addBoolean(String paramName) throws ServletRequestBindingException { | |||
| CriteriaUtils.addBoolean(request, args, paramName); | |||
| return this; | |||
| } | |||
| public Map<String, Object> toMap() { | |||
| return this.args; | |||
| } | |||
| } | |||
| @@ -0,0 +1,191 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| import java.sql.Date; | |||
| import java.util.ArrayList; | |||
| import java.util.Calendar; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import org.springframework.web.bind.ServletRequestBindingException; | |||
| import com.ffii.core.utils.web.ServletRequestUtils; | |||
| /** | |||
| * Utils for getting parameter values from HTTP Request and put them into a criteria map if found | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class CriteriaUtils { | |||
| /** | |||
| * Alias for {@link #addString(HttpServletRequest, Map, String)} | |||
| */ | |||
| public static void addStringExact(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| addString(request, args, paramName); | |||
| } | |||
| /** | |||
| * Alias for {@link #addStringContains(HttpServletRequest, Map, String)} | |||
| */ | |||
| public static void addStringLike(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| addStringContains(request, args, paramName); | |||
| } | |||
| /** | |||
| * Add a String (an exact value for using equal sign in SQL) parameter to criteria. Usually used for exact string search. | |||
| */ | |||
| public static void addString(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| String value = ServletRequestUtils.getTrimmedStringParameter(request, paramName); | |||
| if (value != null) args.put(paramName, value); | |||
| } | |||
| /** | |||
| * Add a String (a substring value for using <code>LIKE</code> in SQL) parameter to criteria. Usually used for substring search for values that contains the | |||
| * input value. | |||
| */ | |||
| public static void addStringContains(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| String value = ServletRequestUtils.getTrimmedStringParameter(request, paramName); | |||
| if (value != null) args.put(paramName, StringUtils.PERCENT + value + StringUtils.PERCENT); | |||
| } | |||
| /** | |||
| * Add a String (a substring value for using <code>LIKE</code> in SQL) parameter to criteria. Usually used for substring search for values that starts with | |||
| * the input value. | |||
| */ | |||
| public static void addStringStartsWith(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| String value = ServletRequestUtils.getTrimmedStringParameter(request, paramName); | |||
| if (value != null) args.put(paramName, value + StringUtils.PERCENT); | |||
| } | |||
| /** | |||
| * Add a String (a substring value for using <code>LIKE</code> in SQL) parameter to criteria. Usually used for substring search for values that ends with | |||
| * the input value. | |||
| */ | |||
| public static void addStringEndsWith(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| String value = ServletRequestUtils.getTrimmedStringParameter(request, paramName); | |||
| if (value != null) args.put(paramName, StringUtils.PERCENT + value); | |||
| } | |||
| /** | |||
| * Add String List parameter to criteria. | |||
| */ | |||
| public static void addStringList(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| String[] params = ServletRequestUtils.getStringParameters(request, paramName); | |||
| if (params.length > 0) { | |||
| List<String> value = new ArrayList<String>(); | |||
| for (int i = 0; i < params.length; i++) | |||
| if (StringUtils.isNotBlank(params[i])) value.add(params[i]); | |||
| if (value.size() > 0) args.put(paramName, value); | |||
| } | |||
| } | |||
| /** | |||
| * Add String CSV parameter to criteria. | |||
| */ | |||
| public static void addStringCsv(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| String text = ServletRequestUtils.getStringParameter(request, paramName); | |||
| if (StringUtils.isNotEmpty(text)) { | |||
| String[] params = text.split(","); | |||
| List<String> values = new ArrayList<String>(); | |||
| for (int i = 0; i < params.length; i++) | |||
| values.add(params[i]); | |||
| args.put(paramName, values); | |||
| } | |||
| } | |||
| /** | |||
| * Add an Integer parameter to criteria. | |||
| */ | |||
| public static void addInteger(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| Integer value = ServletRequestUtils.getIntParameter(request, paramName, null); | |||
| if (value != null) args.put(paramName, value); | |||
| } | |||
| /** | |||
| * Add a non-zero Integer parameter to criteria. | |||
| */ | |||
| public static void addNonZeroInteger(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| Integer value = ServletRequestUtils.getIntParameter(request, paramName, null); | |||
| if (value != null && value.intValue() != 0) args.put(paramName, value); | |||
| } | |||
| /** | |||
| * Add Integer List parameter to criteria. | |||
| */ | |||
| public static void addIntegerList(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| int[] params = ServletRequestUtils.getIntParameters(request, paramName); | |||
| if (params.length > 0) { | |||
| List<Integer> values = new ArrayList<Integer>(); | |||
| for (int i = 0; i < params.length; i++) | |||
| values.add(params[i]); | |||
| args.put(paramName, values); | |||
| } | |||
| } | |||
| /** | |||
| * Add non-zero Integer List parameter to criteria. | |||
| */ | |||
| public static void addNonZeroIntegerList(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| int[] params = ServletRequestUtils.getIntParameters(request, paramName); | |||
| if (params.length > 0) { | |||
| List<Integer> values = new ArrayList<Integer>(); | |||
| for (int i = 0; i < params.length; i++) | |||
| if (params[i] != 0) values.add(params[i]); | |||
| args.put(paramName, values); | |||
| } | |||
| } | |||
| /** | |||
| * Add a Long parameter to criteria. | |||
| */ | |||
| public static void addLong(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| Long value = ServletRequestUtils.getLongParameter(request, paramName, null); | |||
| if (value != null) args.put(paramName, value); | |||
| } | |||
| /** | |||
| * Add a non-zero Long parameter to criteria. | |||
| */ | |||
| public static void addNonZeroLong(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| Long value = ServletRequestUtils.getLongParameter(request, paramName, null); | |||
| if (value != null && value.longValue() != 0L) args.put(paramName, value); | |||
| } | |||
| /** | |||
| * Add a SQL Date parameter to criteria. Usually used for <code>FROM</code> date range search, or exact date search. | |||
| */ | |||
| public static void addDate(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| Date value = ServletRequestUtils.getSqlDateParameter(request, paramName); | |||
| if (value != null) args.put(paramName, value); | |||
| } | |||
| /** | |||
| * Add a SQL Date (plus 1 day) parameter to criteria. Usually used for {@code TO} date range search. | |||
| */ | |||
| public static void addDateTo(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| Date value = ServletRequestUtils.getSqlDateParameter(request, paramName); | |||
| if (value != null) args.put(paramName, DateUtils.add(value.getClass(), value, Calendar.DAY_OF_MONTH, 1)); | |||
| } | |||
| /** | |||
| * Add a Boolean parameter to criteria if it's not {@code null}. Accepts "true", "on", "yes" (any case) and "1" as values for true; treats every other | |||
| * non-empty value as false (i.e. parses leniently). | |||
| */ | |||
| public static void addBoolean(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||
| Boolean value = ServletRequestUtils.getBooleanParameter(request, paramName); | |||
| if (value != null) args.put(paramName, value); | |||
| } | |||
| } | |||
| @@ -0,0 +1,103 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| import java.util.Map; | |||
| import org.apache.commons.lang3.ArrayUtils; | |||
| import org.springframework.beans.MutablePropertyValues; | |||
| import org.springframework.web.bind.WebDataBinder; | |||
| import com.ffii.core.support.SimpleDateEditor; | |||
| import com.ffii.core.support.SimpleIntegerEditor; | |||
| import com.ffii.core.support.SimpleStringTrimToNullEditor; | |||
| /** | |||
| * DataBindUtils | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class DataBindUtils { | |||
| /** coreFields */ | |||
| public static final String[] coreFields = { Params.ID, "ownerId", "deleted", "createdBy", "modifiedBy", "created", "modified", "password" }; | |||
| /** | |||
| * | |||
| * @param object | |||
| * @return WebDataBinder | |||
| */ | |||
| private static WebDataBinder createBinder(Object object) { | |||
| WebDataBinder binder = new WebDataBinder(object); | |||
| binder.registerCustomEditor(String.class, new SimpleStringTrimToNullEditor()); | |||
| binder.registerCustomEditor(Integer.class, new SimpleIntegerEditor()); | |||
| binder.registerCustomEditor(java.util.Date.class, new SimpleDateEditor()); | |||
| return binder; | |||
| } | |||
| /** | |||
| * Binds data using {@link WebDataBinder} with the following custom editors:- | |||
| * | |||
| * <ul> | |||
| * <li>{@link SimpleStringTrimToNullEditor}</li> | |||
| * <li>{@link SimpleIntegerEditor}</li> | |||
| * <li>{@link SimpleDateEditor}</li> | |||
| * </ul> | |||
| * | |||
| * @param record | |||
| * record Map | |||
| * @param object | |||
| * the target object to bind onto | |||
| * @param disallowFields | |||
| * optional | |||
| */ | |||
| public static void bindRecord(Map<String, Object> record, Object object, String... disallowFields) { | |||
| WebDataBinder binder = createBinder(object); | |||
| binder.setDisallowedFields(disallowFields); | |||
| binder.bind(new MutablePropertyValues(record)); | |||
| }; | |||
| /** | |||
| * Binds data using {@link WebDataBinder} with the following custom editors:- | |||
| * | |||
| * <ul> | |||
| * <li>{@link SimpleStringTrimToNullEditor}</li> | |||
| * <li>{@link SimpleIntegerEditor}</li> | |||
| * <li>{@link SimpleDateEditor}</li> | |||
| * </ul> | |||
| * | |||
| * <p> | |||
| * <b>Important:</b> The following system fields will NOT be binded for security reasons. | |||
| * <ul> | |||
| * <li>id</li> | |||
| * <li>ownerId</li> | |||
| * <li>deleted</li> | |||
| * <li>createdBy</li> | |||
| * <li>modifiedBy</li> | |||
| * <li>created</li> | |||
| * <li>modified</li> | |||
| * <li>password</li> | |||
| * </ul> | |||
| * </p> | |||
| * | |||
| * @param record | |||
| * record Map | |||
| * @param object | |||
| * the target object to bind onto | |||
| * @param disallowFields | |||
| * optional | |||
| */ | |||
| public static void bindRecordWithoutCore(Map<String, Object> record, Object object, String... disallowFields) { | |||
| WebDataBinder binder = createBinder(object); | |||
| binder.setDisallowedFields(ArrayUtils.addAll(coreFields, disallowFields)); | |||
| binder.bind(new MutablePropertyValues(record)); | |||
| }; | |||
| } | |||
| @@ -0,0 +1,139 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2012 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| public class Encoding { | |||
| // Supported Encoding Types | |||
| public static final int GB2312 = 0; | |||
| public static final int GBK = 1; | |||
| public static final int GB18030 = 2; | |||
| public static final int HZ = 3; | |||
| public static final int BIG5 = 4; | |||
| public static final int CNS11643 = 5; | |||
| public static final int UTF8 = 6; | |||
| public static final int UTF8T = 7; | |||
| public static final int UTF8S = 8; | |||
| public static final int UNICODE = 9; | |||
| public static final int UNICODET = 10; | |||
| public static final int UNICODES = 11; | |||
| public static final int ISO2022CN = 12; | |||
| public static final int ISO2022CN_CNS = 13; | |||
| public static final int ISO2022CN_GB = 14; | |||
| public static final int EUC_KR = 15; | |||
| public static final int CP949 = 16; | |||
| public static final int ISO2022KR = 17; | |||
| public static final int JOHAB = 18; | |||
| public static final int SJIS = 19; | |||
| public static final int EUC_JP = 20; | |||
| public static final int ISO2022JP = 21; | |||
| public static final int ASCII = 22; | |||
| public static final int OTHER = 23; | |||
| public static final int TOTALTYPES = 24; | |||
| // Names of the encodings as understood by Java | |||
| public static String[] javaname; | |||
| // Names of the encodings for human viewing | |||
| public static String[] nicename; | |||
| // Names of charsets as used in charset parameter of HTML Meta tag | |||
| public static String[] htmlname; | |||
| static { | |||
| javaname = new String[TOTALTYPES]; | |||
| nicename = new String[TOTALTYPES]; | |||
| htmlname = new String[TOTALTYPES]; | |||
| // Assign encoding names | |||
| javaname[GB2312] = "GB2312"; | |||
| javaname[GBK] = "GBK"; | |||
| javaname[GB18030] = "GB18030"; | |||
| javaname[HZ] = "ASCII"; // What to put here? Sun doesn't support HZ | |||
| javaname[ISO2022CN_GB] = "ISO2022CN_GB"; | |||
| javaname[BIG5] = "BIG5"; | |||
| javaname[CNS11643] = "EUC-TW"; | |||
| javaname[ISO2022CN_CNS] = "ISO2022CN_CNS"; | |||
| javaname[ISO2022CN] = "ISO2022CN"; | |||
| javaname[UTF8] = "UTF8"; | |||
| javaname[UTF8T] = "UTF8"; | |||
| javaname[UTF8S] = "UTF8"; | |||
| javaname[UNICODE] = "Unicode"; | |||
| javaname[UNICODET] = "Unicode"; | |||
| javaname[UNICODES] = "Unicode"; | |||
| javaname[EUC_KR] = "EUC_KR"; | |||
| javaname[CP949] = "MS949"; | |||
| javaname[ISO2022KR] = "ISO2022KR"; | |||
| javaname[JOHAB] = "Johab"; | |||
| javaname[SJIS] = "SJIS"; | |||
| javaname[EUC_JP] = "EUC_JP"; | |||
| javaname[ISO2022JP] = "ISO2022JP"; | |||
| javaname[ASCII] = "ASCII"; | |||
| javaname[OTHER] = "ISO8859_1"; | |||
| // Assign encoding names | |||
| htmlname[GB2312] = "GB2312"; | |||
| htmlname[GBK] = "GBK"; | |||
| htmlname[GB18030] = "GB18030"; | |||
| htmlname[HZ] = "HZ-GB-2312"; | |||
| htmlname[ISO2022CN_GB] = "ISO-2022-CN-EXT"; | |||
| htmlname[BIG5] = "BIG5"; | |||
| htmlname[CNS11643] = "EUC-TW"; | |||
| htmlname[ISO2022CN_CNS] = "ISO-2022-CN-EXT"; | |||
| htmlname[ISO2022CN] = "ISO-2022-CN"; | |||
| htmlname[UTF8] = "UTF-8"; | |||
| htmlname[UTF8T] = "UTF-8"; | |||
| htmlname[UTF8S] = "UTF-8"; | |||
| htmlname[UNICODE] = "UTF-16"; | |||
| htmlname[UNICODET] = "UTF-16"; | |||
| htmlname[UNICODES] = "UTF-16"; | |||
| htmlname[EUC_KR] = "EUC-KR"; | |||
| htmlname[CP949] = "x-windows-949"; | |||
| htmlname[ISO2022KR] = "ISO-2022-KR"; | |||
| htmlname[JOHAB] = "x-Johab"; | |||
| htmlname[SJIS] = "Shift_JIS"; | |||
| htmlname[EUC_JP] = "EUC-JP"; | |||
| htmlname[ISO2022JP] = "ISO-2022-JP"; | |||
| htmlname[ASCII] = "ASCII"; | |||
| htmlname[OTHER] = "ISO8859-1"; | |||
| // Assign Human readable names | |||
| nicename[GB2312] = "GB-2312"; | |||
| nicename[GBK] = "GBK"; | |||
| nicename[GB18030] = "GB18030"; | |||
| nicename[HZ] = "HZ"; | |||
| nicename[ISO2022CN_GB] = "ISO2022CN-GB"; | |||
| nicename[BIG5] = "Big5"; | |||
| nicename[CNS11643] = "CNS11643"; | |||
| nicename[ISO2022CN_CNS] = "ISO2022CN-CNS"; | |||
| nicename[ISO2022CN] = "ISO2022 CN"; | |||
| nicename[UTF8] = "UTF-8"; | |||
| nicename[UTF8T] = "UTF-8 (Trad)"; | |||
| nicename[UTF8S] = "UTF-8 (Simp)"; | |||
| nicename[UNICODE] = "Unicode"; | |||
| nicename[UNICODET] = "Unicode (Trad)"; | |||
| nicename[UNICODES] = "Unicode (Simp)"; | |||
| nicename[EUC_KR] = "EUC-KR"; | |||
| nicename[CP949] = "CP949"; | |||
| nicename[ISO2022KR] = "ISO 2022 KR"; | |||
| nicename[JOHAB] = "Johab"; | |||
| nicename[SJIS] = "Shift-JIS"; | |||
| nicename[EUC_JP] = "EUC-JP"; | |||
| nicename[ISO2022JP] = "ISO 2022 JP"; | |||
| nicename[ASCII] = "ASCII"; | |||
| nicename[OTHER] = "OTHER"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,675 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| import java.io.ByteArrayInputStream; | |||
| import java.io.ByteArrayOutputStream; | |||
| import java.io.IOException; | |||
| import java.io.OutputStream; | |||
| import java.math.BigDecimal; | |||
| import java.security.GeneralSecurityException; | |||
| import java.util.Calendar; | |||
| import java.util.Date; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | |||
| import org.apache.poi.openxml4j.opc.OPCPackage; | |||
| import org.apache.poi.poifs.crypt.EncryptionInfo; | |||
| import org.apache.poi.poifs.crypt.EncryptionMode; | |||
| import org.apache.poi.poifs.crypt.Encryptor; | |||
| import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
| import org.apache.poi.ss.usermodel.Cell; | |||
| import org.apache.poi.ss.usermodel.CellType; | |||
| import org.apache.poi.ss.usermodel.DataFormatter; | |||
| import org.apache.poi.ss.usermodel.DateUtil; | |||
| import org.apache.poi.ss.usermodel.RichTextString; | |||
| import org.apache.poi.ss.usermodel.Row; | |||
| import org.apache.poi.ss.usermodel.Sheet; | |||
| import org.apache.poi.ss.usermodel.Workbook; | |||
| import org.apache.poi.ss.util.CellRangeAddress; | |||
| import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |||
| import org.springframework.core.io.Resource; | |||
| import org.springframework.core.io.ResourceLoader; | |||
| /** | |||
| * Excel Utils (for Apache POI 3.15 to 3.17) | |||
| * | |||
| * @author Patrick | |||
| * @version 2018-04-06 | |||
| */ | |||
| public abstract class ExcelUtils { | |||
| /** | |||
| * static A to Z char array | |||
| */ | |||
| private static final char[] A2Z = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', | |||
| 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; | |||
| private static final DataFormatter DATA_FORMATTER = new DataFormatter(); | |||
| /** max rows limit of .xls **/ | |||
| public static final int MAX_ROWS = 65536; | |||
| /** max columns limit of .xls **/ | |||
| public static final int MAX_COLS = 256; | |||
| /** | |||
| * Column reference to index (0-based) map, support up to 256 columns | |||
| * (compatible with .xls format) | |||
| */ | |||
| public static final Map<String, Integer> COL_IDX = new HashMap<String, Integer>(MAX_COLS, 1.0f); | |||
| static { | |||
| for (int columnIndex = 0; columnIndex < MAX_COLS; columnIndex++) { | |||
| int tempColumnCount = columnIndex; | |||
| StringBuilder sb = new StringBuilder(2); | |||
| do { | |||
| sb.insert(0, A2Z[tempColumnCount % 26]); | |||
| tempColumnCount = (tempColumnCount / 26) - 1; | |||
| } while (tempColumnCount >= 0); | |||
| COL_IDX.put(sb.toString(), Integer.valueOf(columnIndex)); | |||
| } | |||
| } | |||
| /** | |||
| * Load XSSF workbook (xlsx file) from template source. | |||
| * | |||
| * @param url the relative path to the template source, e.g. | |||
| * "WEB-INF/excel/exampleReportTemplate.xlsx" | |||
| * | |||
| * @return the workbook, or null if the template file cannot be loaded | |||
| */ | |||
| public static final Workbook loadXSSFWorkbookFromTemplateSource(ResourceLoader resourceLoader, String url) { | |||
| Resource resource = resourceLoader.getResource(url); | |||
| try { | |||
| return new XSSFWorkbook(resource.getInputStream()); | |||
| } catch (IOException e) { | |||
| return null; | |||
| } | |||
| } | |||
| /** | |||
| * Write the workbook to byte array. | |||
| * | |||
| * @param workbook The Excel workbook (cannot be null) | |||
| * | |||
| * @return the byte[], or null if IO exception occurred | |||
| */ | |||
| public static final byte[] toByteArray(Workbook workbook) { | |||
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
| try { | |||
| workbook.write(baos); | |||
| } catch (IOException e) { | |||
| return null; | |||
| } | |||
| return baos.toByteArray(); | |||
| } | |||
| /** | |||
| * Check if the cell exists in the given sheet, row and column. | |||
| * | |||
| * @param sheet the Sheet (cannot be null) | |||
| * @param rowIndex 0-based row index | |||
| * @param colIndex 0-based column index | |||
| * | |||
| * @return {@code true} if cell exists, else {@code false} | |||
| */ | |||
| public static final boolean isCellExists(Sheet sheet, int rowIndex, int colIndex) { | |||
| Row row = sheet.getRow(rowIndex); | |||
| if (row != null) { | |||
| Cell cell = row.getCell(colIndex); | |||
| return cell != null; | |||
| } | |||
| return false; | |||
| } | |||
| /** | |||
| * Convenient method to obtain the cell in the given sheet, row and column. | |||
| * <p> | |||
| * Creates the row and the cell if not already exist. | |||
| * | |||
| * @param sheet the Sheet (cannot be null) | |||
| * @param rowIndex 0-based row index | |||
| * @param colIndex 0-based column index | |||
| * | |||
| * @return the Cell (never null) | |||
| */ | |||
| public static final Cell getCell(Sheet sheet, int rowIndex, int colIndex) { | |||
| Row row = sheet.getRow(rowIndex); | |||
| if (row == null) { | |||
| row = sheet.createRow(rowIndex); | |||
| } | |||
| Cell cell = row.getCell(colIndex); | |||
| if (cell == null) { | |||
| cell = row.createCell(colIndex); | |||
| } | |||
| return cell; | |||
| } | |||
| /** | |||
| * Get column index by column reference (support up to 256 columns) | |||
| * | |||
| * @param columnRef column reference such as "A", "B", "AA", "AB"... | |||
| * | |||
| * @return the column index | |||
| * | |||
| * @throws NullPointerException if column reference is invalid or the index | |||
| * exceeds 256 | |||
| */ | |||
| public static final int getColumnIndex(String columnRef) { | |||
| return COL_IDX.get(columnRef); | |||
| } | |||
| /** | |||
| * Get column reference by column index | |||
| * | |||
| * @param columnIndex 0-based column index | |||
| * | |||
| * @return the column reference such as "A", "B", "AA", "AB"... | |||
| */ | |||
| public static final String getColumnRef(int columnIndex) { | |||
| StringBuilder sb = new StringBuilder(); | |||
| int tempColumnCount = columnIndex; | |||
| do { | |||
| sb.insert(0, A2Z[tempColumnCount % 26]); | |||
| tempColumnCount = (tempColumnCount / 26) - 1; | |||
| } while (tempColumnCount >= 0); | |||
| return sb.toString(); | |||
| } | |||
| /** | |||
| * Get the Excel Cell Ref String by columnIndex and rowIndex | |||
| * | |||
| * @param columnIndex 0-based column index | |||
| * @param rowIndex 0-based row index | |||
| */ | |||
| public static final String getCellRefString(int columnIndex, int rowIndex) { | |||
| StringBuilder sb = new StringBuilder(); | |||
| int tempColumnCount = columnIndex; | |||
| do { | |||
| sb.insert(0, A2Z[tempColumnCount % 26]); | |||
| tempColumnCount = (tempColumnCount / 26) - 1; | |||
| } while (tempColumnCount >= 0); | |||
| sb.append(rowIndex + 1); | |||
| return sb.toString(); | |||
| } | |||
| /** | |||
| * Get Cell value as <code>String</code> | |||
| */ | |||
| public static String getStringValue(Cell cell) { | |||
| if (cell != null && cell.getCellTypeEnum() == CellType.FORMULA) { | |||
| try { | |||
| return cell.getStringCellValue(); | |||
| } catch (Exception e) { | |||
| return ""; | |||
| } | |||
| } | |||
| return DATA_FORMATTER.formatCellValue(cell); | |||
| } | |||
| /** | |||
| * Get Cell value as <code>BigDecimal</code>, with a fallback value | |||
| * <p> | |||
| * Only support {@link Cell#CELL_TYPE_NUMERIC} and {@link Cell#CELL_TYPE_STRING} | |||
| * | |||
| * @return the <code>BigDecimal</code> value, or the default value if cell is | |||
| * <code>null</code> or cell type is {@link Cell#CELL_TYPE_BLANK} | |||
| */ | |||
| public static BigDecimal getDecimalValue(Cell cell, BigDecimal defaultValue) { | |||
| if (cell == null || cell.getCellTypeEnum() == CellType.BLANK) | |||
| return defaultValue; | |||
| if (cell.getCellTypeEnum() == CellType.STRING) { | |||
| return NumberUtils.toDecimal(cell.getStringCellValue()); | |||
| } else { | |||
| return BigDecimal.valueOf(cell.getNumericCellValue()); | |||
| } | |||
| } | |||
| /** | |||
| * Get Cell value as <code>BigDecimal</code> | |||
| * <p> | |||
| * Only support {@link Cell#CELL_TYPE_NUMERIC} and {@link Cell#CELL_TYPE_STRING} | |||
| * | |||
| * @return the <code>BigDecimal</code> value, or <code>BigDecimal.ZERO</code> if | |||
| * cell is <code>null</code> or cell type is | |||
| * {@link Cell#CELL_TYPE_BLANK} | |||
| */ | |||
| public static BigDecimal getDecimalValue(Cell cell) { | |||
| return getDecimalValue(cell, BigDecimal.ZERO); | |||
| } | |||
| /** | |||
| * Get Cell value as <code>double</code> | |||
| * <p> | |||
| * Only support {@link Cell#CELL_TYPE_NUMERIC} and {@link Cell#CELL_TYPE_STRING} | |||
| */ | |||
| public static double getDoubleValue(Cell cell) { | |||
| if (cell == null) | |||
| return 0.0; | |||
| if (cell.getCellTypeEnum() == CellType.STRING) { | |||
| return NumberUtils.toDouble(cell.getStringCellValue()); | |||
| } else { | |||
| return cell.getNumericCellValue(); | |||
| } | |||
| } | |||
| /** | |||
| * Get Cell value as <code>int</code> (rounded half-up to the nearest integer) | |||
| * <p> | |||
| * Only support {@link Cell#CELL_TYPE_NUMERIC} and {@link Cell#CELL_TYPE_STRING} | |||
| */ | |||
| public static int getIntValue(Cell cell) { | |||
| return (int) NumberUtils.round(getDoubleValue(cell), 0); | |||
| } | |||
| /** | |||
| * Get Cell Integer value (truncated) | |||
| */ | |||
| public static Integer getIntValue(Cell cell, Integer defaultValue) { | |||
| if (cell == null) | |||
| return defaultValue; | |||
| if (cell.getCellTypeEnum() == CellType.STRING) { | |||
| return NumberUtils.toInt(cell.getStringCellValue(), defaultValue); | |||
| } else { | |||
| return (int) cell.getNumericCellValue(); | |||
| } | |||
| } | |||
| /** | |||
| * Get Cell Date value | |||
| */ | |||
| public static Date getDateValue(Cell cell) { | |||
| if (cell == null) | |||
| return null; | |||
| if (cell.getCellTypeEnum() == CellType.STRING) { | |||
| return DateUtils.parseDateStrictly(cell.getStringCellValue(), DateUtils.PARSE_PATTERNS, null); | |||
| } | |||
| if (DateUtil.isCellDateFormatted(cell)) { | |||
| try { | |||
| return DateUtil.getJavaDate(cell.getNumericCellValue()); | |||
| } catch (NumberFormatException e) { | |||
| return null; | |||
| } | |||
| } else { | |||
| return null; | |||
| } | |||
| } | |||
| /** | |||
| * Convenient method to set Cell value | |||
| * | |||
| * @param cell the Cell (cannot be null) | |||
| * @param value the value to set | |||
| */ | |||
| public static void setCellValue(Cell cell, Object value) { | |||
| if (value instanceof String) | |||
| cell.setCellValue((String) value); | |||
| else if (value instanceof RichTextString) | |||
| cell.setCellValue((RichTextString) value); | |||
| else if (value instanceof Number) | |||
| cell.setCellValue(((Number) value).doubleValue()); | |||
| else if (value instanceof Boolean) | |||
| cell.setCellValue(((Boolean) value).booleanValue()); | |||
| else if (value instanceof Calendar) | |||
| cell.setCellValue((Calendar) value); | |||
| else if (value instanceof Date) | |||
| cell.setCellValue((Date) value); | |||
| else if (value == null) | |||
| cell.setCellValue(""); | |||
| else | |||
| throw new IllegalArgumentException(value.getClass().toString() + " is not supported"); | |||
| } | |||
| /** | |||
| * Convenient method to set Cell value by Sheet, row index, and column index | |||
| * | |||
| * @param sheet the Sheet (cannot be null) | |||
| * @param rowIndex 0-based row index | |||
| * @param colIndex 0-based column index | |||
| * @param value the value to set | |||
| */ | |||
| public static void setCellValue(Sheet sheet, int rowIndex, int colIndex, Object value) { | |||
| setCellValue(getCell(sheet, rowIndex, colIndex), value); | |||
| } | |||
| /** | |||
| * Increase Row Height (if necessary, but never decrease it) by counting the no. | |||
| * of lines in a String value | |||
| * | |||
| * @param sheet The Excel worksheet | |||
| * @param row The row index (0-based) | |||
| * @param value The (multi-line) String value to count for the no. of | |||
| * lines | |||
| * @param heightInPoints The height (in points) for 1 line of text | |||
| */ | |||
| public static void increaseRowHeight(Sheet sheet, int row, String value, int heightInPoints) { | |||
| int lines = StringUtils.countMatches(value, "\n") + 1; // count no. of lines | |||
| float newHeight = heightInPoints * lines; | |||
| Row r = sheet.getRow(row); | |||
| if (r == null) | |||
| r = sheet.createRow(row); | |||
| // increase the row height if necessary, but never decrease it | |||
| if (r.getHeightInPoints() < newHeight) { | |||
| r.setHeightInPoints(newHeight); | |||
| } | |||
| } | |||
| /** | |||
| * Add merged region (i.e. merge cells) | |||
| * | |||
| * @param sheet The Excel worksheet | |||
| * @param firstRowIdx The first row index (0-based) | |||
| * @param lastRowIdx The last row index (0-based) | |||
| * @param firstColIdx The first column index (0-based) | |||
| * @param lastColIdx The last column index (0-based) | |||
| */ | |||
| public static void addMergedRegion(Sheet sheet, int firstRowIdx, int lastRowIdx, int firstColIdx, int lastColIdx) { | |||
| CellRangeAddress cellRangeAddress = new CellRangeAddress(firstRowIdx, lastRowIdx, firstColIdx, lastColIdx); | |||
| sheet.addMergedRegion(cellRangeAddress); | |||
| } | |||
| /** | |||
| * Copy and Insert Row | |||
| * | |||
| * @param workbook The Excel workbook | |||
| * @param sourceSheet The source Excel worksheet | |||
| * @param destinationSheet The destination Excel worksheet | |||
| * @param sourceRowNum The source row index (0-based) to copy from | |||
| * @param destinationRowNum The destination row index (0-based) to insert into | |||
| * (from the copied row) | |||
| */ | |||
| public static void copyAndInsertRow(Workbook workbook, Sheet sourceSheet, Sheet destinationSheet, int sourceRowNum, | |||
| int destinationRowNum) { | |||
| // get the source / destination row | |||
| Row sourceRow = sourceSheet.getRow(sourceRowNum); | |||
| Row destRow = destinationSheet.getRow(destinationRowNum); | |||
| // if the row exist in destination, push down all rows by 1 | |||
| if (destRow != null) { | |||
| destinationSheet.shiftRows(destinationRowNum, destinationSheet.getLastRowNum(), 1, true, false); | |||
| } | |||
| // create a new row | |||
| destRow = destinationSheet.createRow(destinationRowNum); | |||
| // loop through source columns to add to new row | |||
| for (int i = 0; i < sourceRow.getLastCellNum(); i++) { | |||
| // grab a copy of the old cell | |||
| Cell oldCell = sourceRow.getCell(i); | |||
| // if the old cell is null jump to next cell | |||
| if (oldCell == null) | |||
| continue; | |||
| // create a new cell in destination row | |||
| Cell newCell = destRow.createCell(i); | |||
| // apply cell style to new cell from old cell | |||
| newCell.setCellStyle(oldCell.getCellStyle()); | |||
| // if there is a cell comment, copy | |||
| if (oldCell.getCellComment() != null) { | |||
| newCell.setCellComment(oldCell.getCellComment()); | |||
| } | |||
| // if there is a cell hyperlink, copy | |||
| if (oldCell.getHyperlink() != null) { | |||
| newCell.setHyperlink(oldCell.getHyperlink()); | |||
| } | |||
| // copy the cell data type | |||
| newCell.setCellType(oldCell.getCellTypeEnum()); | |||
| // copy the cell data value | |||
| switch (oldCell.getCellTypeEnum()) { | |||
| case NUMERIC: | |||
| newCell.setCellValue(oldCell.getNumericCellValue()); | |||
| break; | |||
| case STRING: | |||
| newCell.setCellValue(oldCell.getRichStringCellValue()); | |||
| break; | |||
| case FORMULA: | |||
| newCell.setCellFormula(oldCell.getCellFormula()); | |||
| break; | |||
| case BLANK: | |||
| newCell.setCellValue(oldCell.getStringCellValue()); | |||
| break; | |||
| case BOOLEAN: | |||
| newCell.setCellValue(oldCell.getBooleanCellValue()); | |||
| break; | |||
| case ERROR: | |||
| newCell.setCellErrorValue(oldCell.getErrorCellValue()); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| // if there are any merged regions in the source row, copy to new row | |||
| for (int i = 0; i < sourceSheet.getNumMergedRegions(); i++) { | |||
| CellRangeAddress cellRangeAddress = sourceSheet.getMergedRegion(i); | |||
| if (cellRangeAddress.getFirstRow() == sourceRow.getRowNum()) { | |||
| addMergedRegion(destinationSheet, destRow.getRowNum(), | |||
| (destRow.getRowNum() + (cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow())), | |||
| cellRangeAddress.getFirstColumn(), cellRangeAddress.getLastColumn()); | |||
| } | |||
| } | |||
| // copy row height | |||
| destRow.setHeight(sourceRow.getHeight()); | |||
| } | |||
| /** | |||
| * Copy and Insert Row | |||
| * | |||
| * @param workbook The Excel workbook | |||
| * @param sheet The Excel worksheet | |||
| * @param sourceRowNum The source row index (0-based) to copy from | |||
| * @param destinationRowNum The destination row index (0-based) to insert into | |||
| * (from the copied row) | |||
| */ | |||
| public static void copyAndInsertRow(Workbook workbook, Sheet sheet, int sourceRowNum, int destinationRowNum) { | |||
| copyAndInsertRow(workbook, sheet, sheet, sourceRowNum, destinationRowNum); | |||
| } | |||
| /** | |||
| * Copy Column | |||
| * | |||
| * @param workbook The Excel workbook | |||
| * @param sourceSheet The source Excel worksheet | |||
| * @param destinationSheet The destination Excel worksheet | |||
| * @param rowStart The source row start index (0-based) to copy from | |||
| * @param rowEnd The source row end index (0-based) to copy from | |||
| * @param sourceColumnNum The source column index (0-based) to copy from | |||
| * @param destinationColumnNum The destination column index (0-based) to copy | |||
| * into (from the copied row) | |||
| */ | |||
| public static void copyColumn(Workbook workbook, Sheet sourceSheet, Sheet destinationSheet, int rowStart, | |||
| int rowEnd, int sourceColumnNum, int destinationColumnNum) { | |||
| for (int i = rowStart; i <= rowEnd; i++) { | |||
| Row sourceRow = sourceSheet.getRow(i); | |||
| if (sourceRow == null) | |||
| continue; | |||
| Row destinationRow = destinationSheet.getRow(i); | |||
| if (destinationRow == null) | |||
| destinationRow = destinationSheet.createRow(i); | |||
| Cell oldCell = sourceRow.getCell(sourceColumnNum); | |||
| if (oldCell == null) | |||
| continue; | |||
| Cell newCell = destinationRow.createCell(destinationColumnNum); | |||
| newCell.setCellStyle(oldCell.getCellStyle()); | |||
| if (oldCell.getCellComment() != null) { | |||
| newCell.setCellComment(oldCell.getCellComment()); | |||
| } | |||
| if (oldCell.getHyperlink() != null) { | |||
| newCell.setHyperlink(oldCell.getHyperlink()); | |||
| } | |||
| newCell.setCellType(oldCell.getCellTypeEnum()); | |||
| switch (oldCell.getCellTypeEnum()) { | |||
| case NUMERIC: | |||
| newCell.setCellValue(oldCell.getNumericCellValue()); | |||
| break; | |||
| case STRING: | |||
| newCell.setCellValue(oldCell.getRichStringCellValue()); | |||
| break; | |||
| case FORMULA: | |||
| newCell.setCellFormula(oldCell.getCellFormula()); | |||
| break; | |||
| case BLANK: | |||
| newCell.setCellValue(oldCell.getStringCellValue()); | |||
| break; | |||
| case BOOLEAN: | |||
| newCell.setCellValue(oldCell.getBooleanCellValue()); | |||
| break; | |||
| case ERROR: | |||
| newCell.setCellErrorValue(oldCell.getErrorCellValue()); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| for (int ii = 0; ii < sourceSheet.getNumMergedRegions(); ii++) { | |||
| CellRangeAddress cellRangeAddress = sourceSheet.getMergedRegion(ii); | |||
| if (cellRangeAddress.getFirstRow() == sourceRow.getRowNum()) { | |||
| addMergedRegion(destinationSheet, cellRangeAddress.getFirstRow(), cellRangeAddress.getLastRow(), | |||
| destinationColumnNum, (destinationColumnNum | |||
| + (cellRangeAddress.getLastColumn() - cellRangeAddress.getFirstColumn()))); | |||
| } | |||
| } | |||
| } | |||
| destinationSheet.setColumnWidth(destinationColumnNum, sourceSheet.getColumnWidth(sourceColumnNum)); | |||
| } | |||
| /** | |||
| * Copy Column | |||
| * | |||
| * @param workbook The Excel workbook | |||
| * @param sheet The Excel worksheet | |||
| * @param rowStart The source row start index (0-based) to copy from | |||
| * @param rowEnd The source row end index (0-based) to copy from | |||
| * @param sourceColumnNum The source column index (0-based) to copy from | |||
| * @param destinationColumnNum The destination column index (0-based) to copy | |||
| * into (from the copied row) | |||
| */ | |||
| public static void copyColumn(Workbook workbook, Sheet sheet, int rowStart, int rowEnd, int sourceColumnNum, | |||
| int destinationColumnNum) { | |||
| copyColumn(workbook, sheet, sheet, rowStart, rowEnd, sourceColumnNum, destinationColumnNum); | |||
| } | |||
| public static void shiftColumns(Row row, int startingIndex, int shiftCount) { | |||
| for (int i = row.getPhysicalNumberOfCells() - 1; i >= startingIndex; i--) { | |||
| Cell oldCell = row.getCell(i); | |||
| Cell newCell = row.createCell(i + shiftCount); | |||
| // apply cell style to new cell from old cell | |||
| newCell.setCellStyle(oldCell.getCellStyle()); | |||
| // if there is a cell comment, copy | |||
| if (oldCell.getCellComment() != null) { | |||
| newCell.setCellComment(oldCell.getCellComment()); | |||
| } | |||
| // if there is a cell hyperlink, copy | |||
| if (oldCell.getHyperlink() != null) { | |||
| newCell.setHyperlink(oldCell.getHyperlink()); | |||
| } | |||
| // copy the cell data type | |||
| newCell.setCellType(oldCell.getCellTypeEnum()); | |||
| // copy the cell data value | |||
| switch (oldCell.getCellTypeEnum()) { | |||
| case NUMERIC: | |||
| newCell.setCellValue(oldCell.getNumericCellValue()); | |||
| break; | |||
| case STRING: | |||
| newCell.setCellValue(oldCell.getRichStringCellValue()); | |||
| break; | |||
| case FORMULA: | |||
| newCell.setCellFormula(oldCell.getCellFormula()); | |||
| break; | |||
| case BLANK: | |||
| newCell.setCellValue(oldCell.getStringCellValue()); | |||
| break; | |||
| case BOOLEAN: | |||
| newCell.setCellValue(oldCell.getBooleanCellValue()); | |||
| break; | |||
| case ERROR: | |||
| newCell.setCellErrorValue(oldCell.getErrorCellValue()); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| /** handle some invalid char included ( /\*[]:? ) */ | |||
| public static void setSheetName(Workbook workbook, Sheet sheet, String name) { | |||
| if (workbook != null && sheet != null && StringUtils.isNotBlank(name)) | |||
| workbook.setSheetName(workbook.getSheetIndex(sheet), name.replaceAll("[/\\\\*\\[\\]:\\?]", "_")); | |||
| } | |||
| /** delete row */ | |||
| public static void deleteRow(Sheet sheet, int rowIndex) { | |||
| if (sheet != null) { | |||
| sheet.removeRow(sheet.getRow(rowIndex)); | |||
| if (rowIndex < sheet.getLastRowNum()) | |||
| sheet.shiftRows(rowIndex, sheet.getLastRowNum(), -1); | |||
| } | |||
| } | |||
| public static byte[] encrypt(Workbook workbook, String password) { | |||
| return encrypt(toByteArray(workbook), password); | |||
| } | |||
| public static byte[] encrypt(byte[] bytes, String password) { | |||
| POIFSFileSystem fs = new POIFSFileSystem(); | |||
| EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile); | |||
| // EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile, | |||
| // CipherAlgorithm.aes192, HashAlgorithm.sha384, -1, -1, null); | |||
| Encryptor enc = info.getEncryptor(); | |||
| enc.confirmPassword(password); | |||
| // Read in an existing OOXML file and write to encrypted output stream | |||
| // don't forget to close the output stream otherwise the padding bytes aren't | |||
| // added | |||
| try { | |||
| OPCPackage opc = OPCPackage.open(new ByteArrayInputStream(bytes)); | |||
| OutputStream os = enc.getDataStream(fs); | |||
| opc.save(os); | |||
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |||
| fs.writeFilesystem(bos); | |||
| return bos.toByteArray(); | |||
| } catch (InvalidFormatException e) { | |||
| e.printStackTrace(); | |||
| } catch (IOException e) { | |||
| e.printStackTrace(); | |||
| } catch (GeneralSecurityException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| return bytes; | |||
| } | |||
| } | |||
| @@ -0,0 +1,122 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2013 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| /** | |||
| * File Utils | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class FileUtils { | |||
| private static final Map<String, String> MIMETYPES = new HashMap<>(); | |||
| static { | |||
| MIMETYPES.put("pdf", "application/pdf"); | |||
| MIMETYPES.put("doc", "application/msword"); | |||
| MIMETYPES.put("dot", "application/msword"); | |||
| MIMETYPES.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); | |||
| MIMETYPES.put("xls", "application/vnd.ms-excel"); | |||
| MIMETYPES.put("xlm", "application/vnd.ms-excel"); | |||
| MIMETYPES.put("xla", "application/vnd.ms-excel"); | |||
| MIMETYPES.put("xlc", "application/vnd.ms-excel"); | |||
| MIMETYPES.put("xlt", "application/vnd.ms-excel"); | |||
| MIMETYPES.put("xlw", "application/vnd.ms-excel"); | |||
| MIMETYPES.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); | |||
| MIMETYPES.put("ppt", "application/vnd.ms-powerpoint"); | |||
| MIMETYPES.put("pps", "application/vnd.ms-powerpoint"); | |||
| MIMETYPES.put("pot", "application/vnd.ms-powerpoint"); | |||
| MIMETYPES.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"); | |||
| MIMETYPES.put("bat", "application/x-msdownload"); | |||
| MIMETYPES.put("com", "application/x-msdownload"); | |||
| MIMETYPES.put("dll", "application/x-msdownload"); | |||
| MIMETYPES.put("exe", "application/x-msdownload"); | |||
| MIMETYPES.put("msi", "application/x-msdownload"); | |||
| MIMETYPES.put("swf", "application/x-shockwave-flash"); | |||
| MIMETYPES.put("7z", "application/x-7z-compressed"); | |||
| MIMETYPES.put("rar", "application/x-rar-compressed"); | |||
| MIMETYPES.put("zip", "application/zip"); | |||
| MIMETYPES.put("js", "application/javascript"); | |||
| MIMETYPES.put("json", "application/json"); | |||
| MIMETYPES.put("mpga", "audio/mpeg"); | |||
| MIMETYPES.put("mp2", "audio/mpeg"); | |||
| MIMETYPES.put("mp2a", "audio/mpeg"); | |||
| MIMETYPES.put("mp3", "audio/mpeg"); | |||
| MIMETYPES.put("m2a", "audio/mpeg"); | |||
| MIMETYPES.put("m3a", "audio/mpeg"); | |||
| MIMETYPES.put("bmp", "image/bmp"); | |||
| MIMETYPES.put("gif", "image/gif"); | |||
| MIMETYPES.put("jpeg", "image/jpeg"); | |||
| MIMETYPES.put("jpg", "image/jpeg"); | |||
| MIMETYPES.put("jpe", "image/jpeg"); | |||
| MIMETYPES.put("JPG", "image/jpeg"); | |||
| MIMETYPES.put("png", "image/png"); | |||
| MIMETYPES.put("PNG", "image/png"); | |||
| MIMETYPES.put("tiff", "image/tiff"); | |||
| MIMETYPES.put("tif", "image/tiff"); | |||
| MIMETYPES.put("css", "text/css"); | |||
| MIMETYPES.put("csv", "text/csv"); | |||
| MIMETYPES.put("html", "text/html"); | |||
| MIMETYPES.put("htm", "text/html"); | |||
| MIMETYPES.put("txt", "text/plain"); | |||
| MIMETYPES.put("text", "text/plain"); | |||
| MIMETYPES.put("conf", "text/plain"); | |||
| MIMETYPES.put("log", "text/plain"); | |||
| MIMETYPES.put("mp4", "video/mp4"); | |||
| MIMETYPES.put("mp4v", "video/mp4"); | |||
| MIMETYPES.put("mpg4", "video/mp4"); | |||
| MIMETYPES.put("mpeg", "video/mpeg"); | |||
| MIMETYPES.put("mpg", "video/mpeg"); | |||
| MIMETYPES.put("mpe", "video/mpeg"); | |||
| MIMETYPES.put("m1v", "video/mpeg"); | |||
| MIMETYPES.put("m2v", "video/mpeg"); | |||
| MIMETYPES.put("qt", "video/quicktime"); | |||
| MIMETYPES.put("mov", "video/quicktime"); | |||
| MIMETYPES.put("wmv", "video/x-ms-wmv"); | |||
| MIMETYPES.put("wmx", "video/x-ms-wmx"); | |||
| MIMETYPES.put("wvx", "video/x-ms-wvx"); | |||
| MIMETYPES.put("avi", "video/x-msvideo"); | |||
| // MIMETYPES.put("xxxxx", "xxxxx"); | |||
| } | |||
| /** | |||
| * Guess the mimetype from the file name extension | |||
| * | |||
| * @return The mimetype guessed from the file name extension, or {@code null} if the mimetype cannot be determined | |||
| */ | |||
| public static String guessMimetype(String filename) { | |||
| String extension = StringUtils.substringAfterLast(filename, "."); | |||
| String mimetype = MIMETYPES.get(extension); | |||
| return mimetype != null ? mimetype : "application/octet-stream"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| import java.awt.image.BufferedImage; | |||
| import org.imgscalr.Scalr; | |||
| /** | |||
| * Image Utils | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class ImageUtils { | |||
| public BufferedImage resize(BufferedImage srcImage, int targetSize) { | |||
| return Scalr.resize(srcImage, targetSize); | |||
| } | |||
| } | |||
| @@ -0,0 +1,68 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| import java.io.IOException; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import com.fasterxml.jackson.core.JsonParseException; | |||
| import com.fasterxml.jackson.core.JsonProcessingException; | |||
| import com.fasterxml.jackson.core.type.TypeReference; | |||
| import com.fasterxml.jackson.databind.JsonMappingException; | |||
| import com.fasterxml.jackson.databind.ObjectMapper; | |||
| /** | |||
| * JSON Utils | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class JsonUtils { | |||
| // Default mapper instance | |||
| private static final ObjectMapper mapper = new ObjectMapper(); | |||
| /** | |||
| * Method that can be used to serialize any Java value as a JSON String. | |||
| */ | |||
| public static String toJsonString(Object obj) { | |||
| try { | |||
| return mapper.writeValueAsString(obj); | |||
| } catch (JsonProcessingException e) { | |||
| return null; | |||
| } | |||
| } | |||
| /** | |||
| * Read from JSON String. | |||
| * | |||
| * @param content JSON String content | |||
| * @param valueType the return type | |||
| */ | |||
| public static <T> T fromJsonString(String content, Class<T> valueType) | |||
| throws JsonParseException, JsonMappingException, IOException { | |||
| return mapper.readValue(content, valueType); | |||
| } | |||
| public static Map<String, Object> fromJsonStringAsMap(String content) | |||
| throws JsonParseException, JsonMappingException, IOException { | |||
| return mapper.readValue(content, new TypeReference<Map<String, Object>>() { | |||
| }); | |||
| } | |||
| public static List<Map<String, Object>> fromJsonStringAsListOfMap(String content) | |||
| throws JsonParseException, JsonMappingException, IOException { | |||
| return mapper.readValue(content, new TypeReference<List<Map<String, Object>>>() { | |||
| }); | |||
| } | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| package com.ffii.core.utils; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| /** | |||
| * ListUtils | |||
| * | |||
| */ | |||
| public class ListUtils { | |||
| /** | |||
| * Convert values to ArrayList | |||
| * | |||
| * @param values | |||
| * | |||
| * @return List | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <V> List<V> toArrayList(V... values) { | |||
| List<V> list = new ArrayList<V>(values.length); | |||
| for (int i = 0; i < values.length; i++) | |||
| list.add(values[i]); | |||
| return list; | |||
| } | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| import java.util.Locale; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import org.springframework.util.StringUtils; | |||
| import org.springframework.web.servlet.LocaleResolver; | |||
| import org.springframework.web.servlet.i18n.SessionLocaleResolver; | |||
| /** | |||
| * Utility class for performing translation between Locale and String representations. | |||
| */ | |||
| public abstract class LocaleUtils { | |||
| private static final LocaleResolver localeResolver = new SessionLocaleResolver(); | |||
| public static final String toString(Locale locale) { | |||
| return locale.toString(); | |||
| } | |||
| public static final Locale toLocale(String localeString) { | |||
| return StringUtils.parseLocaleString(localeString); | |||
| } | |||
| public static final Locale resolveLocale(HttpServletRequest request) { | |||
| return localeResolver.resolveLocale(request); | |||
| } | |||
| public static final String resolveLocaleString(HttpServletRequest request) { | |||
| return resolveLocale(request).toString(); | |||
| } | |||
| public static final void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { | |||
| localeResolver.setLocale(request, response, locale); | |||
| } | |||
| } | |||
| @@ -0,0 +1,34 @@ | |||
| package com.ffii.core.utils; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| /** | |||
| * @author fung | |||
| */ | |||
| public class MapBuilder<K, V> { | |||
| private Map<K, V> args; | |||
| public MapBuilder() { | |||
| args = new HashMap<K, V>(); | |||
| } | |||
| public MapBuilder(Map<K, V> args) { | |||
| this.args = args; | |||
| } | |||
| public MapBuilder<K, V> add(K key, V value) { | |||
| args.put(key, value); | |||
| return this; | |||
| } | |||
| public Map<K, V> toMap() { | |||
| return args; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return args.toString(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| /** | |||
| * MapUtils | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class MapUtils { | |||
| /** | |||
| * Convert key-value pairs to HashMap | |||
| * | |||
| * @param keyValuePairs | |||
| * Keys and values must be in pairs | |||
| * | |||
| * @return Map | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <K, V> Map<K, V> toHashMap(Object... keyValuePairs) { | |||
| if (keyValuePairs.length % 2 != 0) | |||
| throw new IllegalArgumentException("Keys and values must be in pairs"); | |||
| Map<K, V> map = new HashMap<K, V>(keyValuePairs.length / 2); | |||
| for (int i = 0; i < keyValuePairs.length; i += 2) { | |||
| map.put((K) keyValuePairs[i], (V) keyValuePairs[i + 1]); | |||
| } | |||
| return map; | |||
| } | |||
| } | |||
| @@ -0,0 +1,471 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| import java.math.BigDecimal; | |||
| import java.math.RoundingMode; | |||
| import org.apache.commons.lang3.ArrayUtils; | |||
| /** | |||
| * NumberUtils extends from Apache Commons, and incl some methods from MathUtils | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class NumberUtils extends org.apache.commons.lang3.math.NumberUtils { | |||
| /** | |||
| * Round the given value to the specified number of decimal places. The value is rounded using the given method which is any method defined in | |||
| * {@link BigDecimal}. | |||
| * | |||
| * @param x | |||
| * the value to round | |||
| * @param scale | |||
| * the number of digits to the right of the decimal point | |||
| * @param roundingMethod | |||
| * the rounding method as defined in {@link RoundingMode} | |||
| * @return the rounded value | |||
| */ | |||
| public static double round(double x, int scale, RoundingMode roundingMethod) { | |||
| try { | |||
| return BigDecimal.valueOf(x).setScale(scale, roundingMethod).doubleValue(); | |||
| } catch (NumberFormatException ex) { | |||
| if (Double.isInfinite(x)) { | |||
| return x; | |||
| } else { | |||
| return Double.NaN; | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * Round the given value to the specified number of decimal places. The value is rounded using the {@link RoundingMode#HALF_UP} method. | |||
| * | |||
| * @param x | |||
| * the value to round | |||
| * @param scale | |||
| * the number of digits to the right of the decimal point | |||
| * @return the rounded value | |||
| * @see org.apache.commons.math.util.MathUtils#round(double, int) | |||
| */ | |||
| public static double round(double x, int scale) { | |||
| return round(x, scale, RoundingMode.HALF_UP); | |||
| } | |||
| /** | |||
| * Round up the given value to the specified number of decimal places. The value is rounded up using the {@link RoundingMode#UP} method. | |||
| * | |||
| * @param x | |||
| * the value to round up | |||
| * @param scale | |||
| * the number of digits to the right of the decimal point | |||
| * @return the rounded up value | |||
| */ | |||
| public static double roundUp(double x, int scale) { | |||
| return round(x, scale, RoundingMode.UP); | |||
| } | |||
| /** | |||
| * Round down the given value to the specified number of decimal places. The value is rounded down using the {@link RoundingMode#DOWN} method. | |||
| * | |||
| * @param x | |||
| * the value to round down | |||
| * @param scale | |||
| * the number of digits to the right of the decimal point | |||
| * @return the rounded down value | |||
| */ | |||
| public static double roundDown(double x, int scale) { | |||
| return round(x, scale, RoundingMode.DOWN); | |||
| } | |||
| /** | |||
| * Return the {@code int} value of an {@code Object}, or the default value if the object is either {@code null} or not an instance of {@code Number}. | |||
| * | |||
| * @param obj | |||
| * the {@code Object} | |||
| * @param defaultValue | |||
| * the default value | |||
| * @return the {@code int} value of the {@code Object}, or the default if the object is either {@code null} or not an instance of {@code Number} | |||
| * @see Number#intValue() | |||
| */ | |||
| public static int intValue(Object obj, int defaultValue) { | |||
| if (obj instanceof Number) { | |||
| return ((Number) obj).intValue(); | |||
| } else if (obj instanceof String) { | |||
| try { | |||
| return Integer.parseInt((String) obj); | |||
| } catch (NumberFormatException nfe) { | |||
| } | |||
| } | |||
| return defaultValue; | |||
| } | |||
| /** | |||
| * Return the {@code int} value of an {@code Object}, or {@code zero} if the object is either {@code null} or not an instance of {@code Number}. | |||
| * | |||
| * @param obj | |||
| * the {@code Object} | |||
| * @return the {@code int} value of the {@code Object}, or {@code zero} if the object is either {@code null} or not an instance of {@code Number} | |||
| * @see Number#intValue() | |||
| */ | |||
| public static int intValue(Object obj) { | |||
| return intValue(obj, 0); | |||
| } | |||
| /** | |||
| * Convert an {@code Object} to an {@code Integer} (only if the object is an instance of {@code Number} or {@code String}), returning a default value if the | |||
| * conversion fails. | |||
| * <p> | |||
| * If the object is {@code null}, the default value is returned. | |||
| * | |||
| * @param obj | |||
| * the object to convert, may be {@code null} | |||
| * @param defaultValue | |||
| * the default value, may be {@code null} | |||
| * @return the Integer represented by the object, or the default if conversion fails | |||
| */ | |||
| public static Integer toInt(Object obj, Integer defaultValue) { | |||
| if (obj instanceof Number) | |||
| return Integer.valueOf(((Number) obj).intValue()); | |||
| else if (obj instanceof String) | |||
| try { | |||
| return Integer.valueOf((String) obj); | |||
| } catch (NumberFormatException nfe) { | |||
| return defaultValue; | |||
| } | |||
| else | |||
| return defaultValue; | |||
| } | |||
| /** | |||
| * Return the {@code long} value of a {@code Long} object, or a default value if the object is {@code null}. | |||
| * | |||
| * @param obj | |||
| * the object (can be {@code null}) | |||
| * @param defaultValue | |||
| * the default value | |||
| * @return the {@code long} value of the object if it's a number, or the default value if the object is {@code null} or {@code NaN} | |||
| * @see Long#longValue() | |||
| */ | |||
| public static long longValue(Object obj, long defaultValue) { | |||
| return (obj instanceof Number) ? ((Number) obj).longValue() : defaultValue; | |||
| } | |||
| /** | |||
| * Return the {@code long} value of a {@code Long} object, or {@code zero} if the object is {@code null}. | |||
| * | |||
| * @param obj | |||
| * the object (can be {@code null}) | |||
| * @return the {@code long} value of the object if it's a number, or {@code zero} if the object is {@code null} or {@code NaN} | |||
| * @see Long#longValue() | |||
| */ | |||
| public static long longValue(Object obj) { | |||
| return longValue(obj, 0l); | |||
| } | |||
| /** | |||
| * Convert an {@code Object} to a {@code Long} (only if the object is an instance of {@code Number} or {@code String}), returning a default value if the | |||
| * conversion fails. | |||
| * <p> | |||
| * If the object is {@code null}, the default value is returned. | |||
| * | |||
| * @param obj | |||
| * the object to convert, may be {@code null} | |||
| * @param defaultValue | |||
| * the default value, may be {@code null} | |||
| * @return the Long represented by the object, or the default if conversion fails | |||
| */ | |||
| public static Long toLong(Object obj, Long defaultValue) { | |||
| if (obj instanceof Number) | |||
| return Long.valueOf(((Number) obj).longValue()); | |||
| else if (obj instanceof String) | |||
| try { | |||
| return Long.valueOf((String) obj); | |||
| } catch (NumberFormatException nfe) { | |||
| return defaultValue; | |||
| } | |||
| else | |||
| return defaultValue; | |||
| } | |||
| /** | |||
| * @param obj | |||
| * the object (can be {@code null}) | |||
| * @param defaultValue | |||
| * the default value | |||
| * @return the {@code double} value of the object if it's a number, or the default value if the object is {@code null} or {@code NaN} | |||
| * @see Double#doubleValue() | |||
| */ | |||
| public static double doubleValue(Object obj, double defaultValue) { | |||
| return (obj instanceof Number) ? ((Number) obj).doubleValue() : defaultValue; | |||
| } | |||
| /** | |||
| * @param obj | |||
| * the object (can be {@code null}) | |||
| * @param defaultValue | |||
| * the default value | |||
| * @return the {@code double} value of the object if it's a number, or {@code zero} if the object is {@code null} or {@code NaN} | |||
| * @see Double#doubleValue() | |||
| */ | |||
| public static double doubleValue(Object obj) { | |||
| return doubleValue(obj, 0.0d); | |||
| } | |||
| /** | |||
| * Convert an {@code Object} to a {@code Double} (only if the object is an instance of {@code Number} or {@code String}), returning a default value if the | |||
| * conversion fails. | |||
| * <p> | |||
| * If the object is {@code null}, the default value is returned. | |||
| * | |||
| * @param obj | |||
| * the object to convert, may be {@code null} | |||
| * @param defaultValue | |||
| * the default value, may be {@code null} | |||
| * @return the Double represented by the object, or the default if conversion fails | |||
| */ | |||
| public static Double toDouble(Object obj, Double defaultValue) { | |||
| if (obj instanceof Number) | |||
| return Double.valueOf(((Number) obj).doubleValue()); | |||
| else if (obj instanceof String) | |||
| try { | |||
| return Double.valueOf((String) obj); | |||
| } catch (NumberFormatException nfe) { | |||
| return defaultValue; | |||
| } | |||
| else | |||
| return defaultValue; | |||
| } | |||
| /** | |||
| * Return the {@code BigDecimal} object, or {@code zero} if the object is {@code null}. | |||
| * | |||
| * @param obj | |||
| * the {@code BigDecimal} object | |||
| * @return the {@code BigDecimal} object, or {@code zero} if the object is {@code null} | |||
| */ | |||
| public static BigDecimal decimalValue(BigDecimal obj) { | |||
| return decimalValue(obj, BigDecimal.ZERO); | |||
| } | |||
| /** | |||
| * Return the {@code BigDecimal} object, or a default value if the object is {@code null}. | |||
| * | |||
| * @param obj | |||
| * the {@code BigDecimal} object | |||
| * @param defaultValue | |||
| * the default value | |||
| * @return the {@code BigDecimal} object, or the default if the object is {@code null} | |||
| */ | |||
| public static BigDecimal decimalValue(BigDecimal obj, BigDecimal defaultValue) { | |||
| return obj == null ? defaultValue : obj; | |||
| } | |||
| /** | |||
| * Convert an {@code Object} to a {@code BigDecimal}, returning {@code BigDecimal.ZERO} if the conversion fails (e.g. the object is not an instance of | |||
| * {@code Number} nor {@code String}). | |||
| * <p> | |||
| * If the object is {@code null}, {@code BigDecimal.ZERO} is returned. | |||
| * | |||
| * @param obj | |||
| * the object to convert, may be {@code null} | |||
| * @return the BigDecimal represented by the object, or {@code BigDecimal.ZERO} if conversion fails | |||
| */ | |||
| public static BigDecimal toDecimal(Object obj) { | |||
| return toDecimal(obj, BigDecimal.ZERO); | |||
| } | |||
| /** | |||
| * Convert an {@code Object} to a {@code BigDecimal}, returning a default value if the conversion fails (e.g. the object is not an instance of | |||
| * {@code Number} nor {@code String}). | |||
| * <p> | |||
| * If the object is {@code null}, the default value is returned. | |||
| * | |||
| * @param obj | |||
| * the object to convert, may be {@code null} | |||
| * @param defaultValue | |||
| * the default value, may be {@code null} | |||
| * @return the BigDecimal represented by the object, or the default if conversion fails | |||
| */ | |||
| public static BigDecimal toDecimal(Object obj, BigDecimal defaultValue) { | |||
| if (obj instanceof BigDecimal) | |||
| return (BigDecimal) obj; | |||
| else if (obj instanceof Number) | |||
| return BigDecimal.valueOf(((Number) obj).doubleValue()); | |||
| else if (obj instanceof String) | |||
| try { | |||
| return new BigDecimal((String) obj); | |||
| } catch (NumberFormatException nfe) { | |||
| return defaultValue; | |||
| } | |||
| else | |||
| return defaultValue; | |||
| } | |||
| /** | |||
| * Null-safe method to check if the two {@code Integer} objects have the same value. | |||
| * | |||
| * <ol> | |||
| * <li>Returns {@code true} if {@code a} and {@code b} are both {@code null}. | |||
| * <li>Returns {@code false} if only one of them is {@code null}. | |||
| * <li>Returns {@code true} if {@code a} and {@code b} are not {@code null} and have the same {@code int} value, else returns {@code false}. | |||
| * </ol> | |||
| * | |||
| * @param a | |||
| * Integer obj, may be {@code null} | |||
| * @param b | |||
| * Integer obj, may be {@code null} | |||
| */ | |||
| public static boolean isEqual(Integer a, Integer b) { | |||
| return a == null ? (b == null ? true : false) : (b == null ? false : a.equals(b)); | |||
| } | |||
| /** | |||
| * Null-safe method to check if the two {@code Integer} objects have different values. | |||
| * | |||
| * <ol> | |||
| * <li>Returns {@code false} if {@code a} and {@code b} are both {@code null}. | |||
| * <li>Returns {@code true} if only one of them is {@code null}. | |||
| * <li>Returns {@code true} if {@code a} and {@code b} are not {@code null} and have different {@code int} values, else returns {@code false}. | |||
| * </ol> | |||
| * | |||
| * @param a | |||
| * Integer obj, may be {@code null} | |||
| * @param b | |||
| * Integer obj, may be {@code null} | |||
| */ | |||
| public static boolean isNotEqual(Integer a, Integer b) { | |||
| return !isEqual(a, b); | |||
| } | |||
| /** | |||
| * Null-safe method to check if the two {@code BigDecimal} objects have the same value. | |||
| * <p> | |||
| * Two {@code BigDecimal} objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. | |||
| * | |||
| * <ol> | |||
| * <li>Returns {@code true} if {@code a} and {@code b} are both {@code null}. | |||
| * <li>Returns {@code false} if only one of them is {@code null}. | |||
| * <li>Returns {@code true} if {@code a} and {@code b} are not {@code null} and have the same {@code decimal} value, else returns {@code false}. | |||
| * </ol> | |||
| * | |||
| * @param a | |||
| * BigDecimal obj, may be {@code null} | |||
| * @param b | |||
| * BigDecimal obj, may be {@code null} | |||
| */ | |||
| public static boolean isEqual(BigDecimal a, BigDecimal b) { | |||
| return a == null ? (b == null ? true : false) : (b == null ? false : a.compareTo(b) == 0); | |||
| } | |||
| /** | |||
| * Null-safe method to check if the two {@code BigDecimal} objects have different values. | |||
| * <p> | |||
| * Two {@code BigDecimal} objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. | |||
| * | |||
| * <ol> | |||
| * <li>Returns {@code false} if {@code a} and {@code b} are both {@code null}. | |||
| * <li>Returns {@code true} if only one of them is {@code null}. | |||
| * <li>Returns {@code true} if {@code a} and {@code b} are not {@code null} and have different {@code decimal} values, else returns {@code false}. | |||
| * </ol> | |||
| * | |||
| * @param a | |||
| * BigDecimal obj, may be {@code null} | |||
| * @param b | |||
| * BigDecimal obj, may be {@code null} | |||
| */ | |||
| public static boolean isNotEqual(BigDecimal a, BigDecimal b) { | |||
| return !isEqual(a, b); | |||
| } | |||
| /** | |||
| * Check if {@code BigDecimal} object {@code a} is greater than {@code BigDecimal} object {@code b}. | |||
| * | |||
| * @param a | |||
| * non-{@code null} BigDecimal obj | |||
| * @param b | |||
| * non-{@code null} BigDecimal obj | |||
| */ | |||
| public static boolean isGreaterThan(BigDecimal a, BigDecimal b) { | |||
| return a.compareTo(b) > 0; | |||
| } | |||
| /** | |||
| * Check if {@code BigDecimal} object {@code a} is greater than or equals to {@code BigDecimal} object {@code b}. | |||
| * | |||
| * @param a | |||
| * non-{@code null} BigDecimal obj | |||
| * @param b | |||
| * non-{@code null} BigDecimal obj | |||
| */ | |||
| public static boolean isGreaterThanOrEqual(BigDecimal a, BigDecimal b) { | |||
| return a.compareTo(b) >= 0; | |||
| } | |||
| /** | |||
| * Check if {@code BigDecimal} object {@code a} is less than {@code BigDecimal} object {@code b}. | |||
| * | |||
| * @param a | |||
| * non-{@code null} BigDecimal obj | |||
| * @param b | |||
| * non-{@code null} BigDecimal obj | |||
| */ | |||
| public static boolean isLessThan(BigDecimal a, BigDecimal b) { | |||
| return a.compareTo(b) < 0; | |||
| } | |||
| /** | |||
| * Check if {@code BigDecimal} object {@code a} is less than or equals to {@code BigDecimal} object {@code b}. | |||
| * | |||
| * @param a | |||
| * non-{@code null} BigDecimal obj | |||
| * @param b | |||
| * non-{@code null} BigDecimal obj | |||
| */ | |||
| public static boolean isLessThanOrEqual(BigDecimal a, BigDecimal b) { | |||
| return a.compareTo(b) <= 0; | |||
| } | |||
| /** | |||
| * | |||
| * <pre> | |||
| * NumberUtils.equalsAny(null, (Integer[]) null) = false | |||
| * NumberUtils.equalsAny(null, null, null) = true | |||
| * NumberUtils.equalsAny(null, 1, 2) = false | |||
| * NumberUtils.equalsAny(1, null, 2) = false | |||
| * NumberUtils.equalsAny(1, 1, 2) = true | |||
| * </pre> | |||
| * | |||
| * @param int | |||
| * to compare, may be {@code null}. | |||
| * @param searchInts | |||
| * a int, may be {@code null}. | |||
| * @return {@code true} if the num is equal to any other element of <code>searchInts</code>; {@code false} if <code>searchInts</code> is null or contains no | |||
| * matches. | |||
| */ | |||
| public static boolean equalsAny(final int num, int... searchInts) { | |||
| if (ArrayUtils.isNotEmpty(searchInts)) { | |||
| for (int next : searchInts) { | |||
| if (num == next) { | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| public static double sum(double... nums) { | |||
| BigDecimal rs = BigDecimal.ZERO; | |||
| for (double num : nums) | |||
| rs = rs.add(BigDecimal.valueOf(num)); | |||
| return rs.doubleValue(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,81 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| /** | |||
| * Static strings for standard params | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class Params { | |||
| public static final String ID = "id"; | |||
| public static final String TYPE = "type"; | |||
| public static final String QUERY = "query"; | |||
| public static final String CODE = "code"; | |||
| public static final String NAME = "name"; | |||
| public static final String TITLE = "title"; | |||
| public static final String KEY = "key"; | |||
| public static final String VALUE = "value"; | |||
| public static final String SUCCESS = "success"; | |||
| public static final String AUTH = "auth"; | |||
| /** Short for "message" */ | |||
| public static final String MSG = "msg"; | |||
| public static final String DETAILS = "details"; | |||
| public static final String DATA = "data"; | |||
| public static final String RECORDS = "records"; | |||
| public static final String ROOT = "root"; | |||
| public static final String NODE = "node"; | |||
| public static final String EXPANDED = "expanded"; | |||
| public static final String FROM = "from"; | |||
| public static final String TO = "to"; | |||
| public static final String START = "start"; | |||
| public static final String LIMIT = "limit"; | |||
| public static final String PREFIX = "prefix"; | |||
| public static final String SUFFIX = "suffix"; | |||
| public static final String LENGTH = "length"; | |||
| public static final String MODE = "mode"; | |||
| public static final String COUNT = "count"; | |||
| public static final String TOTAL = "total"; | |||
| public static final String STATUS = "status"; | |||
| public static final String VERSION_ID = "versionId"; | |||
| public static final String REF_ID = "refId"; | |||
| public static final String REF_TYPE = "refType"; | |||
| public static final String REF_CODE = "refCode"; | |||
| public static final String FROM_DATE = "fromDate"; | |||
| public static final String TO_DATE = "toDate"; | |||
| public static final String MAX_ROWS = "maxRows"; | |||
| public static final String METHOD_GET = "GET"; | |||
| public static final String METHOD_POST = "POST"; | |||
| } | |||
| @@ -0,0 +1,225 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import java.util.Random; | |||
| import java.util.regex.Pattern; | |||
| import com.ffii.core.User; | |||
| import com.ffii.tbms.user.service.UserService; | |||
| import org.springframework.dao.DataAccessException; | |||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | |||
| import org.springframework.security.core.Authentication; | |||
| import org.springframework.security.core.GrantedAuthority; | |||
| import org.springframework.security.core.context.SecurityContext; | |||
| import org.springframework.security.core.context.SecurityContextHolder; | |||
| import org.springframework.security.core.userdetails.UserDetails; | |||
| import org.springframework.security.core.userdetails.UserDetailsService; | |||
| import org.springframework.security.core.userdetails.UsernameNotFoundException; | |||
| /** | |||
| * Security Utils - for Spring Security | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public class SecurityUtils { | |||
| private static final Pattern PATTERN_DIGITS = Pattern.compile("[0-9]"); | |||
| private static final Pattern PATTERN_A2Z_LOWER = Pattern.compile("[a-z]"); | |||
| private static final Pattern PATTERN_A2Z_UPPER = Pattern.compile("[A-Z]"); | |||
| private static final String A2Z_LOWER = "abcdefghijklmnopqrstuvwxyz"; | |||
| private static final String A2Z_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |||
| private static final String DIGITS = "0123456789"; | |||
| /* | |||
| * Ref: https://www.owasp.org/index.php/Password_special_characters | |||
| * without space character | |||
| */ | |||
| private static final String SPECIAL_CHARS = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; | |||
| private static Pattern PATTERN_SPECIAL_CHARS = Pattern.compile("[!\"#$%&'()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~]"); | |||
| /** | |||
| * Obtains the current {@code SecurityContext}. | |||
| * | |||
| * @return the security context (never {@code null}) | |||
| */ | |||
| public static final SecurityContext getSecurityContext() { | |||
| return SecurityContextHolder.getContext(); | |||
| } | |||
| /** | |||
| * @return the authenticated {@code Principal} ({@code User}) | |||
| * @see Authentication#getPrincipal() | |||
| */ | |||
| public static final User getUser() { | |||
| try { | |||
| return (User) getSecurityContext().getAuthentication().getPrincipal(); | |||
| } catch (ClassCastException e) { | |||
| // no authenticated principal | |||
| return null; | |||
| } catch (NullPointerException e) { | |||
| // no authentication information is available | |||
| return null; | |||
| } | |||
| } | |||
| /** | |||
| * Updates the Authentication Token with the user (e.g. user changed the password) | |||
| * | |||
| * @see SecurityContext#setAuthentication(Authentication) | |||
| */ | |||
| public static final void updateUserAuthentication(final User user) { | |||
| getSecurityContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities())); | |||
| } | |||
| /** | |||
| * Checks if the current user is GRANTED the {@code role} | |||
| * | |||
| * @param role | |||
| * the {@code role} to check for | |||
| * @return {@code true} if the current user is GRANTED the {@code role}, else {@code false} | |||
| */ | |||
| public static final boolean isGranted(String role) { | |||
| Authentication authentication = getSecurityContext().getAuthentication(); | |||
| if (authentication == null) return false; | |||
| for (GrantedAuthority auth : authentication.getAuthorities()) { | |||
| if (auth.getAuthority().equals("SUPERUSER")) return true; | |||
| if (role.equals(auth.getAuthority())) return true; | |||
| } | |||
| return false; | |||
| } | |||
| public static final boolean isGrantedOnly(String role) { | |||
| Authentication authentication = getSecurityContext().getAuthentication(); | |||
| if (authentication == null) return false; | |||
| for (GrantedAuthority auth : authentication.getAuthorities()) { | |||
| if (role.equals(auth.getAuthority())) return true; | |||
| } | |||
| return false; | |||
| } | |||
| /** | |||
| * Checks if the current user is NOT GRANTED the {@code role} | |||
| * | |||
| * @param role | |||
| * the {@code role} to check for | |||
| * @return {@code true} if the current user is NOT GRANTED the {@code role}, else {@code false} | |||
| */ | |||
| public static final boolean isNotGranted(String role) { | |||
| return !isGranted(role); | |||
| } | |||
| /** | |||
| * Checks if the current user is GRANTED ANY of the {@code role}s | |||
| * | |||
| * @param roles | |||
| * the {@code role}s to check for | |||
| * @return {@code true} if the current user is GRANTED ANY of the {@code role}s, else {@code false} | |||
| */ | |||
| public static final boolean isGrantedAny(String... roles) { | |||
| for (int i = 0; i < roles.length; i++) { | |||
| if (isGranted(roles[i])) return true; | |||
| } | |||
| return false; | |||
| } | |||
| /** | |||
| * Checks if the current user is NOT GRANTED ANY of the {@code role}s | |||
| * | |||
| * @param roles | |||
| * the {@code role}s to check for | |||
| * @return {@code true} if the current user is NOT GRANTED ANY of the {@code role}s, else {@code false} | |||
| */ | |||
| public static final boolean isNotGrantedAny(String... roles) { | |||
| return !isGrantedAny(roles); | |||
| } | |||
| /** | |||
| * Checks if the current user is GRANTED ALL of the {@code role}s | |||
| * | |||
| * @param roles | |||
| * the {@code role}s to check for | |||
| * @return {@code true} if the current user is GRANTED ALL of the {@code role}s, else {@code false} | |||
| */ | |||
| public static final boolean isGrantedAll(String... roles) { | |||
| for (int i = 0; i < roles.length; i++) { | |||
| if (isNotGranted(roles[i])) return false; | |||
| } | |||
| return true; | |||
| } | |||
| /** | |||
| * Login a user non-interactively | |||
| * | |||
| * @param userService | |||
| * any implementation of {@link UserDetailsService} | |||
| * @param username | |||
| * the username | |||
| * | |||
| * @throws UsernameNotFoundException | |||
| * if the user could not be found or the user has no GrantedAuthority | |||
| * @throws DataAccessException | |||
| * if user could not be found for a repository-specific reason | |||
| */ | |||
| public static final void loginUser(UserDetailsService userService, String username) { | |||
| /* load the user, throw exception if user not found */ | |||
| UserDetails userDetails = userService.loadUserByUsername(username); | |||
| /* create authentication token for the specified user */ | |||
| Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); | |||
| getSecurityContext().setAuthentication(authentication); | |||
| } | |||
| public static final boolean checkPwd(String pwd, int min, int max, boolean num, boolean upperEng, boolean lowerEng, boolean special) { | |||
| if (pwd == null) return false; | |||
| if (pwd.length() < min) return false; | |||
| if (pwd.length() > max) return false; | |||
| if (num && !PATTERN_DIGITS.matcher(pwd).find()) return false; | |||
| if (upperEng && !PATTERN_A2Z_UPPER.matcher(pwd).find()) return false; | |||
| if (lowerEng && !PATTERN_A2Z_LOWER.matcher(pwd).find()) return false; | |||
| if (special && !PATTERN_SPECIAL_CHARS.matcher(pwd).find()) return false; | |||
| return true; | |||
| } | |||
| public static String genPwd(int length, boolean num, boolean upperEng, boolean lowerEng, boolean special) { | |||
| if (length <= 0) return ""; | |||
| StringBuilder password = new StringBuilder(length); | |||
| Random random = new Random(System.nanoTime()); | |||
| List<String> charCategories = new ArrayList<>(4); | |||
| if (lowerEng) charCategories.add(A2Z_LOWER); | |||
| if (upperEng) charCategories.add(A2Z_UPPER); | |||
| if (num) charCategories.add(DIGITS); | |||
| if (special) charCategories.add(SPECIAL_CHARS); | |||
| for (int i = 0; i < length; i++) { | |||
| String charCategory = charCategories.get(i % charCategories.size()); | |||
| char randomChar = charCategory.charAt(random.nextInt(charCategory.length())); | |||
| if (password.length() > 0) | |||
| password.insert(random.nextInt(password.length()), randomChar); | |||
| else | |||
| password.append(randomChar); | |||
| } | |||
| return password.toString(); | |||
| } | |||
| public static void authArgs(Map<String, Object> args){ | |||
| args.put("sysGroupId", getUser().getSysGroupId()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| package com.ffii.core.utils; | |||
| public class SqlUtils { | |||
| /** | |||
| * for IN() of SQL. if no parameter, it will return "''" | |||
| * | |||
| * @return 'obj1','obj2','obj3','obj4'... | |||
| */ | |||
| public static String toInString(Object... objs) { | |||
| String rs = ""; | |||
| for (int i = 0; i < objs.length; i++) { | |||
| if (i != 0) rs += ","; | |||
| rs += "'" + objs[i] + "'"; | |||
| } | |||
| if (StringUtils.isBlank(rs)) | |||
| rs += "''"; | |||
| return rs; | |||
| } | |||
| } | |||
| @@ -0,0 +1,71 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| /** | |||
| * String Utils based on Apache Commons StringUtils. | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class StringUtils extends org.apache.commons.lang3.StringUtils { | |||
| /** | |||
| * The String {@code "0"}. | |||
| */ | |||
| public static final String ZERO = "0"; | |||
| /** | |||
| * The String {@code "1"}. | |||
| */ | |||
| public static final String ONE = "1"; | |||
| /** | |||
| * The String {@code "%"}. | |||
| */ | |||
| public static final String PERCENT = "%"; | |||
| /** | |||
| * The String {@code ","}. | |||
| */ | |||
| public static final String COMMA = ","; | |||
| /** | |||
| * The String {@code "\r\n"} for line break on Windows | |||
| */ | |||
| public static final String LINE_BREAK_WINDOWS = "\r\n"; | |||
| /** | |||
| * The String {@code "\n"} for line break on Unix/Linux | |||
| */ | |||
| public static final String LINE_BREAK_LINUX = "\n"; | |||
| public static final String[] A2Z_LOWWER = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", | |||
| "w", "x", "y", "z" }; | |||
| public static final String[] A2Z_UPPER = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", | |||
| "W", "X", "Y", "Z" }; | |||
| public static final String concat(String segregator, final String... chars) { | |||
| if (segregator == null) segregator = ""; | |||
| String rs = ""; | |||
| for (String c : chars) { | |||
| if (c == null) | |||
| continue; | |||
| else { | |||
| if (StringUtils.isBlank(rs)) { | |||
| rs = c; | |||
| } else { | |||
| rs += segregator + c; | |||
| } | |||
| } | |||
| } | |||
| return rs; | |||
| } | |||
| } | |||
| @@ -0,0 +1,54 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core2 project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils; | |||
| import java.awt.Image; | |||
| import com.google.zxing.BarcodeFormat; | |||
| import com.google.zxing.Writer; | |||
| import com.google.zxing.WriterException; | |||
| import com.google.zxing.client.j2se.MatrixToImageWriter; | |||
| import com.google.zxing.common.BitMatrix; | |||
| import com.google.zxing.oned.Code128Writer; | |||
| import com.google.zxing.qrcode.QRCodeWriter; | |||
| /** | |||
| * ZXing Utils | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class ZXingUtils { | |||
| private static final Writer CODE128_WRITER = new Code128Writer(); | |||
| private static final Writer QR_CODE_WRITER = new QRCodeWriter(); | |||
| /** | |||
| * Encode contents (String) to Code 128 image | |||
| * | |||
| * @throws WriterException | |||
| */ | |||
| public static Image encodeToImageCode128(String contents, int width, int height) throws WriterException { | |||
| BitMatrix matrix = CODE128_WRITER.encode(contents, BarcodeFormat.CODE_128, width, height); | |||
| return MatrixToImageWriter.toBufferedImage(matrix); | |||
| } | |||
| /** | |||
| * Encode contents (String) to QR Code image | |||
| * | |||
| * @throws WriterException | |||
| */ | |||
| public static Image encodeToImageQRCode(String contents, int width, int height) throws WriterException { | |||
| BitMatrix matrix = QR_CODE_WRITER.encode(contents, BarcodeFormat.QR_CODE, width, height); | |||
| return MatrixToImageWriter.toBufferedImage(matrix); | |||
| } | |||
| } | |||
| @@ -0,0 +1,114 @@ | |||
| package com.ffii.core.utils.sql; | |||
| import java.util.Map; | |||
| import com.ffii.core.utils.StringUtils; | |||
| /** | |||
| * <p>a query builder for handling paging </p> | |||
| * <p><b>suffix cannot blank</b></p> | |||
| * <p> | |||
| * paging arguments key: | |||
| * "start": integer (base at 0), | |||
| * "limit": integer | |||
| * </p> | |||
| * | |||
| * @see #setPaging(boolean) | |||
| * @see #setPrefix(String) | |||
| * @see #setSuffix(String) | |||
| * | |||
| * @@author Fung | |||
| */ | |||
| public class QueryBuilder implements java.io.Serializable { | |||
| private static final long serialVersionUID = 4433680400177304974L; | |||
| // setting | |||
| private boolean paging = false; | |||
| private Map<String, Object> args; | |||
| /** "SELECT *" */ | |||
| private String prefix; | |||
| /** "FROM xxxxxxx WHERE true" */ | |||
| private String suffix; | |||
| /** default disable paging */ | |||
| public QueryBuilder(){} | |||
| public QueryBuilder(boolean paging){ | |||
| this.paging = paging; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return getSearchSql(); | |||
| } | |||
| public String getSearchSql() { | |||
| parameterCheck(); | |||
| return getPrefix() + " " + getSuffix() + (this.paging ? limitQuery() : ""); | |||
| } | |||
| /** column name "count" */ | |||
| public String getTotalSql() { | |||
| parameterCheck(); | |||
| return "SELECT COUNT(1) 'count'" + " " + getSuffix(); | |||
| } | |||
| private void parameterCheck() { | |||
| if (StringUtils.isBlank(suffix)) | |||
| throw new IllegalArgumentException("suffix of sql cannot blank"); | |||
| } | |||
| private String limitQuery() { | |||
| if (args == null) | |||
| return StringUtils.EMPTY; | |||
| else if (args.containsKey("start") && args.containsKey("limit")) | |||
| return " LIMIT :start,:limit"; | |||
| else if (args.containsKey("limit")) | |||
| return " LIMIT :limit"; | |||
| else | |||
| return StringUtils.EMPTY; | |||
| } | |||
| // setter and getter | |||
| public boolean isPaging() { | |||
| return this.paging; | |||
| } | |||
| public boolean getPaging() { | |||
| return this.paging; | |||
| } | |||
| /** true for paging query, default false*/ | |||
| public void setPaging(boolean paging) { | |||
| this.paging = paging; | |||
| } | |||
| public Map<String, Object> getArgs() { | |||
| return this.args; | |||
| } | |||
| public void setArgs(Map<String, Object> args) { | |||
| this.args = args; | |||
| } | |||
| public String getPrefix() { | |||
| return StringUtils.isNotBlank(this.prefix) ? this.prefix : "SELECT *"; | |||
| } | |||
| /** "SELECT *" */ | |||
| public void setPrefix(String prefix) { | |||
| this.prefix = prefix; | |||
| } | |||
| public String getSuffix() { | |||
| return this.suffix; | |||
| } | |||
| /** <p>"FROM xxxxxxxx WHERE true"</p>suffix cannot blank*/ | |||
| public void setSuffix(String suffix) { | |||
| this.suffix = suffix; | |||
| } | |||
| } | |||
| @@ -0,0 +1,336 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils.web; | |||
| import java.io.IOException; | |||
| import java.util.Collections; | |||
| import java.util.Date; | |||
| import java.util.Enumeration; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import javax.servlet.ServletRequest; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import com.ffii.core.support.SimpleDateEditor; | |||
| import com.ffii.core.support.SimpleIntegerEditor; | |||
| import com.ffii.core.support.SimpleStringTrimToNullEditor; | |||
| import com.ffii.core.utils.DateUtils; | |||
| import com.ffii.core.utils.JsonUtils; | |||
| import com.ffii.core.utils.Params; | |||
| import com.ffii.core.utils.StringUtils; | |||
| import org.apache.commons.lang3.ArrayUtils; | |||
| import org.springframework.web.bind.ServletRequestBindingException; | |||
| import org.springframework.web.bind.ServletRequestDataBinder; | |||
| /** | |||
| * ServletRequestUtils (extends from Spring Framework) | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class ServletRequestUtils extends org.springframework.web.bind.ServletRequestUtils { | |||
| /** | |||
| * Mobile User-Agent Signatures | |||
| */ | |||
| private static final String[] MOBILE_USER_AGENTS = { "Android", "iPhone", "iPad" }; | |||
| /** | |||
| * Get the <b>User-Agent</b> from the request header | |||
| * | |||
| * @param request | |||
| * HttpServletRequest | |||
| * | |||
| * @return the <b>User-Agent</b> from the request header, or <code>null</code> if not found | |||
| */ | |||
| public static String getUserAgent(HttpServletRequest request) { | |||
| return request.getHeader("User-Agent"); | |||
| } | |||
| /** | |||
| * Check if the <b>User-Agent</b> is mobile device | |||
| * | |||
| * @param request | |||
| * HttpServletRequest | |||
| * | |||
| * @return <code>true</code> if the <b>User-Agent</b> is mobile device | |||
| */ | |||
| public static boolean isMobileUserAgent(HttpServletRequest request) { | |||
| for (String MOBILE_USER_AGENT : MOBILE_USER_AGENTS) { | |||
| if (StringUtils.containsIgnoreCase(getUserAgent(request), MOBILE_USER_AGENT)) | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| /** | |||
| * Get a Map of request parameters | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * | |||
| * @return a Map containing all of the request parameters | |||
| */ | |||
| public static Map<String, String> getParameters(ServletRequest request) { | |||
| Enumeration<String> params = request.getParameterNames(); | |||
| Map<String, String> paramsMap = new HashMap<String, String>(); | |||
| while (params.hasMoreElements()) { | |||
| String paramName = (String) params.nextElement(); | |||
| String paramValue = request.getParameter(paramName); | |||
| paramsMap.put(paramName, paramValue); | |||
| } | |||
| return paramsMap; | |||
| } | |||
| /** | |||
| * Get an Integer parameter, with a fallback value. Never throws an exception. Can pass a distinguished value as default to enable checks of whether it was | |||
| * supplied. | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param name | |||
| * the name of the parameter | |||
| * @param defaultVal | |||
| * the default value to use as fallback | |||
| * | |||
| * @return the Integer value, or <code>defaultVal</code> if not present OR if the value cannot be parsed | |||
| */ | |||
| public static Integer getIntParameter(ServletRequest request, String name, Integer defaultVal) { | |||
| try { | |||
| return getIntParameter(request, name); | |||
| } catch (ServletRequestBindingException e) { | |||
| return defaultVal; | |||
| } | |||
| } | |||
| /** | |||
| * Get a Long parameter, with a fallback value. Never throws an exception. Can pass a distinguished value as default to enable checks of whether it was | |||
| * supplied. | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param name | |||
| * the name of the parameter | |||
| * @param defaultVal | |||
| * the default value to use as fallback | |||
| * | |||
| * @return the Long value, or <code>defaultVal</code> if not present OR if the value cannot be parsed | |||
| */ | |||
| public static Long getLongParameter(ServletRequest request, String name, Long defaultVal) { | |||
| try { | |||
| return getLongParameter(request, name); | |||
| } catch (ServletRequestBindingException e) { | |||
| return defaultVal; | |||
| } | |||
| } | |||
| /** | |||
| * Get a Double parameter, with a fallback value. Never throws an exception. Can pass a distinguished value as default to enable checks of whether it was | |||
| * supplied. | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param name | |||
| * the name of the parameter | |||
| * @param defaultVal | |||
| * the default value to use as fallback | |||
| * | |||
| * @return the Double value, or <code>defaultVal</code> if not present OR if the value cannot be parsed | |||
| */ | |||
| public static Double getDoubleParameter(ServletRequest request, String name, Double defaultVal) { | |||
| try { | |||
| return getDoubleParameter(request, name); | |||
| } catch (ServletRequestBindingException e) { | |||
| return defaultVal; | |||
| } | |||
| } | |||
| /** | |||
| * Get a trimmed (to <code>null</code> if blank) String parameter, or <code>null</code> if not present. | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param name | |||
| * the name of the parameter | |||
| * | |||
| * @return the trimmed (to <code>null</code> if blank) String value, or <code>null</code> if not present | |||
| * | |||
| * @throws ServletRequestBindingException | |||
| * a subclass of ServletException, so it doesn't need to be caught | |||
| */ | |||
| public static String getTrimmedStringParameter(ServletRequest request, String name) throws ServletRequestBindingException { | |||
| return StringUtils.trimToNull(org.springframework.web.bind.ServletRequestUtils.getStringParameter(request, name)); | |||
| } | |||
| /** | |||
| * Get a trimmed (to <code>null</code> if blank) String parameter, with a fallback value. Never throws an exception. Can pass a distinguished value to | |||
| * default to enable checks of whether it was supplied. | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param name | |||
| * the name of the parameter | |||
| * @param defaultVal | |||
| * the default value to use as fallback | |||
| * | |||
| * @return the trimmed (to <code>null</code> if blank) String value, or <code>defaultVal</code> (will be trimmed to <code>null</code> if blank) if not | |||
| * present | |||
| */ | |||
| public static String getTrimmedStringParameter(ServletRequest request, String name, String defaultVal) { | |||
| return StringUtils.trimToNull(org.springframework.web.bind.ServletRequestUtils.getStringParameter(request, name, defaultVal)); | |||
| } | |||
| /** | |||
| * Get a SQL Date parameter, or <code>null</code> if not present or cannot be parsed. | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param name | |||
| * the name of the parameter | |||
| * @param defaultVal | |||
| * the default value to use as fallback | |||
| * | |||
| * @return the SQL Date value, or <code>null</code> if not present or cannot be parsed | |||
| */ | |||
| public static java.sql.Date getSqlDateParameter(ServletRequest request, String name) throws ServletRequestBindingException { | |||
| return getSqlDateParameter(request, name, null); | |||
| } | |||
| /** | |||
| * Get a SQL Date parameter, with a fallback value. | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param name | |||
| * the name of the parameter | |||
| * @param defaultVal | |||
| * the default value to use as fallback | |||
| * | |||
| * @return the SQL Date value, or default value if not present or cannot be parsed | |||
| */ | |||
| public static java.sql.Date getSqlDateParameter(ServletRequest request, String name, java.sql.Date defaultVal) throws ServletRequestBindingException { | |||
| return DateUtils.toSqlDate(DateUtils.parseDateStrictly((getTrimmedStringParameter(request, name)), DateUtils.PARSE_PATTERNS, defaultVal)); | |||
| } | |||
| /** | |||
| * Get a Date parameter, or <code>null</code> if not present or cannot be parsed. | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param name | |||
| * the name of the parameter | |||
| * @param defaultVal | |||
| * the default value to use as fallback | |||
| * | |||
| * @return the Date value, or <code>null</code> if not present or cannot be parsed | |||
| */ | |||
| public static Date getDateParameter(ServletRequest request, String name) throws ServletRequestBindingException { | |||
| return getDateParameter(request, name, null); | |||
| } | |||
| /** | |||
| * Get a Date parameter, with a fallback value. | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param name | |||
| * the name of the parameter | |||
| * @param defaultVal | |||
| * the default value to use as fallback | |||
| * | |||
| * @return the Date value, or default value if not present or cannot be parsed | |||
| */ | |||
| public static Date getDateParameter(ServletRequest request, String name, Date defaultVal) throws ServletRequestBindingException { | |||
| return DateUtils.parseDateStrictly((getTrimmedStringParameter(request, name)), DateUtils.PARSE_PATTERNS, defaultVal); | |||
| } | |||
| /** | |||
| * Get a List of Map from JSON string parameter. | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param name | |||
| * the name of the parameter | |||
| * | |||
| * @return a List of Map, or an empty List if the parameter is not present | |||
| */ | |||
| public static List<?> getJsonListParameter(ServletRequest request, String name) throws ServletRequestBindingException, IOException { | |||
| String content = getStringParameter(request, name); | |||
| if (content != null) | |||
| return JsonUtils.fromJsonString(content, List.class); | |||
| else | |||
| return Collections.EMPTY_LIST; | |||
| } | |||
| /** | |||
| * Get a Map from JSON string parameter. | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param name | |||
| * the name of the parameter | |||
| * | |||
| * @return a Map, or an empty Map if the parameter is not present | |||
| */ | |||
| public static Map<?,?> getJsonMapParameter(ServletRequest request, String name) throws ServletRequestBindingException, IOException { | |||
| String content = getStringParameter(request, name); | |||
| if (content != null) | |||
| return JsonUtils.fromJsonString(content, Map.class); | |||
| else | |||
| return Collections.EMPTY_MAP; | |||
| } | |||
| /** | |||
| * Binds data using {@link ServletRequestDataBinder} with the following custom editors:- | |||
| * | |||
| * <ul> | |||
| * <li>{@link SimpleStringTrimToNullEditor}</li> | |||
| * <li>{@link SimpleIntegerEditor}</li> | |||
| * <li>{@link SimpleDateEditor}</li> | |||
| * </ul> | |||
| * | |||
| * <p> | |||
| * <b>Important:</b> The following system fields will NOT be binded for security reasons. | |||
| * <ul> | |||
| * <li>id</li> | |||
| * <li>ownerId</li> | |||
| * <li>deleted</li> | |||
| * <li>createdBy</li> | |||
| * <li>modifiedBy</li> | |||
| * <li>created</li> | |||
| * <li>modified</li> | |||
| * <li>password</li> | |||
| * </ul> | |||
| * </p> | |||
| * | |||
| * @param request | |||
| * current HTTP request | |||
| * @param object | |||
| * the target object to bind onto | |||
| * @param objectName | |||
| * the name of the target object | |||
| * @param disallowFields | |||
| * optional | |||
| */ | |||
| public static Object doBind(ServletRequest request, Object object, String objectName, String... disallowFields) { | |||
| ServletRequestDataBinder binder = new ServletRequestDataBinder(object, objectName); | |||
| binder.registerCustomEditor(String.class, new SimpleStringTrimToNullEditor()); | |||
| binder.registerCustomEditor(Integer.class, new SimpleIntegerEditor()); | |||
| binder.registerCustomEditor(java.util.Date.class, new SimpleDateEditor()); | |||
| String[] coreFields = { Params.ID, "ownerId", "deleted", "createdBy", "modifiedBy", "created", "modified", "password" }; | |||
| binder.setDisallowedFields(ArrayUtils.addAll(coreFields, disallowFields)); | |||
| binder.bind(request); | |||
| return object; | |||
| }; | |||
| } | |||
| @@ -0,0 +1,72 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.utils.web; | |||
| import java.io.IOException; | |||
| import java.io.OutputStreamWriter; | |||
| import java.io.UnsupportedEncodingException; | |||
| import javax.servlet.ServletOutputStream; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| /** | |||
| * ServletResponseUtils | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| public abstract class ServletResponseUtils { | |||
| /** | |||
| * Writes a String to HttpServletResponse encoded with the specified charsetName, and then flush and close it. | |||
| * | |||
| * @param response | |||
| * HttpServletResponse | |||
| * @param charsetName | |||
| * The name of a supported {@link java.nio.charset.Charset </code>charset<code>} | |||
| * @param str | |||
| * String to be written | |||
| */ | |||
| public static void writeStringToStream(HttpServletResponse response, String charsetName, String str) throws UnsupportedEncodingException, IOException { | |||
| OutputStreamWriter out = new OutputStreamWriter(response.getOutputStream(), charsetName); | |||
| out.write(str); | |||
| out.flush(); | |||
| out.close(); | |||
| } | |||
| /** | |||
| * Writes bytes to HttpServletResponse, and then flush and close it. | |||
| * | |||
| * @param response | |||
| * HttpServletResponse | |||
| * @param bytes | |||
| * The bytes array to be written | |||
| * @throws IOException | |||
| */ | |||
| public static void writeBytesToStream(HttpServletResponse response, byte[] bytes) throws IOException { | |||
| ServletOutputStream out = response.getOutputStream(); | |||
| out.write(bytes); | |||
| out.flush(); | |||
| out.close(); | |||
| } | |||
| /** | |||
| * Disables caching by modifying the response header. | |||
| * | |||
| * @param response | |||
| * HttpServletResponse | |||
| */ | |||
| public static void disableCaching(HttpServletResponse response) { | |||
| response.addHeader("Pragma", "no-cache"); | |||
| response.addHeader("Cache-Control", "no-cache, no-store, max-age=0"); | |||
| response.addDateHeader("Expires", 1L); | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.web; | |||
| import org.springframework.web.context.support.WebApplicationObjectSupport; | |||
| public abstract class AbstractController extends WebApplicationObjectSupport { | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.web; | |||
| import org.springframework.web.context.support.WebApplicationObjectSupport; | |||
| public abstract class AbstractService extends WebApplicationObjectSupport { | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| package com.ffii.core.web; | |||
| import org.springframework.context.annotation.Configuration; | |||
| import org.springframework.transaction.annotation.EnableTransactionManagement; | |||
| @Configuration | |||
| @EnableTransactionManagement | |||
| public class AppConfig { | |||
| } | |||
| @@ -0,0 +1,35 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.web; | |||
| import org.springframework.web.bind.WebDataBinder; | |||
| import org.springframework.web.bind.annotation.InitBinder; | |||
| import com.ffii.core.support.SimpleDateEditor; | |||
| import com.ffii.core.support.SimpleIntegerEditor; | |||
| import com.ffii.core.support.SimpleStringTrimToNullEditor; | |||
| /** | |||
| * ControllerAdvice is used to define @ExceptionHandler, @InitBinder, and @ModelAttribute methods that apply to all @RequestMapping methods. | |||
| * | |||
| * @author Patrick | |||
| */ | |||
| @org.springframework.web.bind.annotation.ControllerAdvice | |||
| public class ControllerAdvice { | |||
| @InitBinder | |||
| public void initBinder(WebDataBinder binder) { | |||
| binder.registerCustomEditor(String.class, new SimpleStringTrimToNullEditor()); | |||
| binder.registerCustomEditor(Integer.class, new SimpleIntegerEditor()); | |||
| binder.registerCustomEditor(java.util.Date.class, new SimpleDateEditor()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.web; | |||
| import org.apache.commons.lang3.exception.ExceptionUtils; | |||
| import org.apache.commons.logging.Log; | |||
| import org.apache.commons.logging.LogFactory; | |||
| import org.springframework.dao.DataIntegrityViolationException; | |||
| import org.springframework.http.HttpStatus; | |||
| import org.springframework.http.ResponseEntity; | |||
| import org.springframework.web.bind.annotation.ExceptionHandler; | |||
| import org.springframework.web.context.request.WebRequest; | |||
| import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; | |||
| /** | |||
| * @author Patrick | |||
| */ | |||
| @org.springframework.web.bind.annotation.ControllerAdvice | |||
| public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { | |||
| protected final Log logger = LogFactory.getLog(getClass()); | |||
| @ExceptionHandler(value = { DataIntegrityViolationException.class }) | |||
| protected ResponseEntity<Object> handleDataIntegrityViolationException(RuntimeException ex, WebRequest request) { | |||
| logger.error(ex.getMessage()); | |||
| return handleExceptionInternal(ex, ExceptionUtils.getRootCauseMessage(ex), null, HttpStatus.INTERNAL_SERVER_ERROR, request); | |||
| } | |||
| } | |||
| @@ -0,0 +1,52 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.web.filter; | |||
| import java.io.IOException; | |||
| import javax.servlet.Filter; | |||
| import javax.servlet.FilterChain; | |||
| import javax.servlet.FilterConfig; | |||
| 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; | |||
| public class AjaxSessionExpirationFilter implements Filter { | |||
| private int customSessionExpiredErrorCode = 403; | |||
| @Override | |||
| public void init(FilterConfig cfg) throws ServletException { | |||
| } | |||
| @Override | |||
| public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { | |||
| HttpSession currentSession = ((HttpServletRequest) request).getSession(false); | |||
| if (currentSession == null) { | |||
| String requestedWithHeader = ((HttpServletRequest) request).getHeader("X-Requested-With"); | |||
| if ("XMLHttpRequest".equals(requestedWithHeader)) { | |||
| ((HttpServletResponse) response).sendError(this.customSessionExpiredErrorCode); | |||
| } else { | |||
| chain.doFilter(request, response); | |||
| } | |||
| } else { | |||
| chain.doFilter(request, response); | |||
| } | |||
| } | |||
| @Override | |||
| public void destroy() { | |||
| } | |||
| } | |||
| @@ -0,0 +1,46 @@ | |||
| /******************************************************************************* | |||
| * Copyright 2019 2Fi Business Solutions Ltd. | |||
| * | |||
| * This code is part of the Core project. | |||
| * | |||
| * This code is copyrighted. Under no circumstances should any party, people, | |||
| * or organization should redistribute any portions of this code in any form, | |||
| * either verbatim or through electronic media, to any third parties, unless | |||
| * under explicit written permission by 2Fi Business Solutions Ltd. | |||
| ******************************************************************************/ | |||
| package com.ffii.core.web.view; | |||
| import org.springframework.web.servlet.View; | |||
| /** | |||
| * Abstract base class extending Spring {@link AbstractView} for {@link View} implementations. | |||
| * | |||
| * @see View | |||
| * @author Patrick | |||
| */ | |||
| public abstract class AbstractView extends org.springframework.web.servlet.view.AbstractView { | |||
| public static final String CONTENT_TYPE_JSON = "application/json"; | |||
| public static final String CONTENT_TYPE_JAVASCRIPT = "text/javascript"; | |||
| public static final String CONTENT_TYPE_TEXT_HTML = "text/html"; | |||
| public static final String CONTENT_TYPE_TEXT_PLAIN = "text/plain"; | |||
| public static final String CONTENT_TYPE_XML = "text/xml"; | |||
| public static final String CONTENT_TYPE_JPEG = "image/jpeg"; | |||
| public static final String CONTENT_TYPE_PNG = "image/png"; | |||
| public static final String CHARSET_UTF8 = "UTF-8"; | |||
| protected boolean disableCaching = true; | |||
| /** | |||
| * Disables caching of the generated JSON. <br> | |||
| * Default is {@code true}, which will prevent the client from caching the generated JSON. | |||
| */ | |||
| public void setDisableCaching(boolean disableCaching) { | |||
| this.disableCaching = disableCaching; | |||
| } | |||
| } | |||