@@ -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; | |||
} | |||
} |