Compare commits

...

231 Commits

Author SHA1 Message Date
Lukasz Lenart a249ed5f31 [maven-release-plugin] prepare release STRUTS_2_5_11 2017-06-22 13:11:07 +02:00
Lukasz Lenart 5d999d6ac1 Adds a constant to control when proxy can be accessed 2017-06-22 09:01:35 +02:00
Lukasz Lenart 8f53b6f59e WW-4744 WW-4694 Removes annotation search to commons lang 3.6 2017-06-22 08:40:46 +02:00
Aleksandr Mashchenko 0d6442bab5 add constant to control proxy member access 2017-06-22 00:58:41 +03:00
Yasser Zamani 33e1eeb386 WW-4744 WW-4694 Removes annotation search to commons lang 3.6 2017-06-21 13:28:45 +04:30
Yasser Zamani fa20b71fc2 WW-4794 Fixes wrapField's where clause 2017-06-20 16:42:08 +04:30
Lukasz Lenart 4c386c663c WW-4805 Blocks ognl access to class members of Spring proxy 2017-06-20 12:44:58 +02:00
Lukasz Lenart 31f0252e60 Marks class as deprecated 2017-06-20 09:45:55 +02:00
Lukasz Lenart 05f3b7a9b9 WW-4793 Reduces overhead with looking for a FileManager 2017-06-20 09:30:51 +02:00
Lukasz Lenart 93dba5bce2 WW-4758 Uses ObjectFactory to create Tiles' ViewPrepare 2017-06-18 11:11:25 +02:00
Lukasz Lenart 216c52095e Cleans up code 2017-06-18 11:11:25 +02:00
Yasser Zamani 6478815291 WW-4805 Improves ProxyUtil performance via caching 2017-06-18 13:15:53 +04:30
Yasser Zamani 7987c38d5a Improves two previous commit, by performance and bug safety 2017-06-14 18:14:19 +04:30
Yasser Zamani 56f31bec42 Hides Spring concepts behind ProxyUtil 2017-06-14 15:35:29 +04:30
Yasser Zamani 2a8a6869d0 Blocks ognl access to class members of Spring proxy 2017-06-13 18:48:23 +04:30
Aleksandr Mashchenko 843693fdd4 fix confusing debug message 2017-06-05 23:20:26 +03:00
Lukasz Lenart 0100a6bebf WW-4801 fixes duplicated hidden field in checkboxListHandler 2017-06-05 08:51:19 +02:00
gregh 46153e78da WW-4801 Only one hidden field per checkboxlist. 2017-05-30 15:02:23 +01:00
Yasser Zamani 8e9f9fb89f WW-4800 Executes aspects when chaining AOPed actions 2017-05-29 18:56:10 +04:30
Lukasz Lenart 2fb431d97e WW-3171 WW-3650 WW-4581 makes number converters locale aware 2017-05-18 15:29:33 +02:00
Lukasz Lenart 3a9f86a605 WW-4762 Uses global to better express the meaning 2017-05-18 10:18:07 +02:00
Lukasz Lenart cb0f42abcc WW-4762 Adds additiona LocalizedTextProvider which uses only default bundles 2017-05-18 10:09:33 +02:00
Lukasz Lenart 8b862f7cb2 WW-4105 Adds support for Spring proxies when chaining actions 2017-05-17 19:49:21 +02:00
Lukasz Lenart 51afa63183 Supports string to float conversion based on locale 2017-05-17 07:18:22 +02:00
Lukasz Lenart 45aae9cd80 Cleans up imports and simplifies if clause 2017-05-12 16:36:30 +02:00
Lukasz Lenart 229afea64e WW-3171 Uses proper number of digits when formatting double 2017-05-12 15:58:54 +02:00
Lukasz Lenart 20eced9500 WW-3171 Converts numbers to strings using locale 2017-05-12 14:40:20 +02:00
Lukasz Lenart 266d78d32c WW-3650 Supports double conversion for different locale 2017-05-12 13:48:14 +02:00
Lukasz Lenart f874f9cde5 WW-4581 Uses proper logic to convert String to BigDecimal 2017-05-12 12:19:48 +02:00
Lukasz Lenart 5a6c336947 Uses proper package 2017-05-12 09:28:03 +02:00
Lukasz Lenart 7fa41f1885 WW-4110 Marks AOP support as deprecated 2017-05-12 08:28:56 +02:00
Yasser Zamani 9a8b4f4ebc WW-4105 Removes SpringUtils to ProxyUtil 2017-05-11 12:49:46 +04:30
Lukasz Lenart 5a0f2e1aaf WW-4749 Implements buffer/flush behaviour in FreemarkerResult 2017-05-10 16:39:41 +02:00
Lukasz Lenart 4604333649 WW-4762 Defines default-only bean 2017-05-10 15:48:35 +02:00
Lukasz Lenart fd168c423f WW-4762 Extracts common method 2017-05-10 14:12:39 +02:00
Lukasz Lenart 7360b5e195 WW-4762 Moves common method up 2017-05-10 13:59:20 +02:00
Lukasz Lenart b842a4c779 WW-4749 Renames local variable to better express its meaning 2017-05-10 10:08:48 +02:00
Lukasz Lenart 0314ddcb5c WW-4795 Implements @LongRangeFieldValidator annotation 2017-05-09 10:33:35 +02:00
Yasser Zamani c2f2de06ce WW-4105 Unwraps Spring proxy in actions chain 2017-04-30 09:20:57 +04:30
Lukasz Lenart 38a2ebb72a WW-4749 Uses writeIfCompleted to keep backward compatibility 2017-04-28 13:14:30 +02:00
Lukasz Lenart 3034b3a970 WW-4749 Uses Boolean to allow define behaviour per result 2017-04-28 13:00:09 +02:00
Lukasz Lenart 6dad53c5e3 WW-4749 Fixes test 2017-04-28 10:01:27 +02:00
Lukasz Lenart bbcd9dc251 WW-4749 Uses ordinary boolean instead of Boolean 2017-04-28 09:33:56 +02:00
Lukasz Lenart 1df89792f9 WW-4749 Drops writeIfCompleted and uses Boolean instead String 2017-04-28 09:32:47 +02:00
Lukasz Lenart bbd4a9e8c5 WW-4792 Marks XWork constants as deprecated 2017-04-26 20:29:33 +02:00
Lukasz Lenart bb19a62098 WW-4762 Introduces default provider which only uses default bundles 2017-04-26 20:28:11 +02:00
Lukasz Lenart 2e23d7a07e WW-4762 Extracts base abstract class 2017-04-26 12:24:57 +02:00
Lukasz Lenart 8bf77a1d19 WW-4762 Uses Struts prefix for the main implementation 2017-04-26 11:56:48 +02:00
Lukasz Lenart 3ffc9b8450 WW-4762 Drops unused imports 2017-04-26 11:56:09 +02:00
Lukasz Lenart 45d1a49131 WW-4762 Drops unused code 2017-04-26 11:55:49 +02:00
Lukasz Lenart b46a6e7956 Drops static modifier 2017-04-26 11:39:22 +02:00
Lukasz Lenart f06caf7ca7 WW-4791 Stops using DefaultLocalizedTextProvider#localeFromString 2017-04-26 10:38:32 +02:00
Lukasz Lenart f5125bcd1b WW-4749 Defines setter to allow specify useBufferedWriter 2017-04-26 08:51:45 +02:00
Lukasz Lenart 787150d041 Moves documentation to wiki 2017-04-26 08:48:01 +02:00
Lukasz Lenart d739be34bc WW-4749 Implements buffered write 2017-04-26 08:42:42 +02:00
Lukasz Lenart 8f75e1a19d WW-4149 Introduces an empty stack 2017-04-26 08:22:19 +02:00
Lukasz Lenart 775d1c8c65 WW-3952 introduces credit card validator 2017-04-24 10:12:13 +02:00
Lukasz Lenart 6ef9f7a3aa WW-3952 Fixes test 2017-04-23 10:31:13 +02:00
Lukasz Lenart faf72b5a09 WW-4210 Adds support for defining converter via class instead of string 2017-04-22 17:42:15 +02:00
Lukasz Lenart 9eec07b310 WW-3952 Adds client side validation support 2017-04-22 17:40:40 +02:00
Aleksandr Mashchenko d8284846db WW-4788 Parameters which are added via ServletDispatcherResult aren't availabe in #parameters 2017-04-22 12:15:00 +03:00
Stefaan Dutry b465791dc2 WW-4780 fixed logging in the struts2-portlet-plugin tests 2017-04-22 00:05:41 +02:00
Stefaan Dutry defaf43a4c WW-4780 fixed logging in the struts2-cdi-plugin tests 2017-04-21 23:49:43 +02:00
Stefaan Dutry 11cccb68a9 upgraded version of log4j2 to 2.8.2 2017-04-20 22:06:20 +02:00
Lukasz Lenart 9512410412 WW-4787 Moves test to src/test/java 2017-04-20 14:35:11 +02:00
Lukasz Lenart a58452571c WW-4210 Updates JavaDocs 2017-04-20 08:34:55 +02:00
Lukasz Lenart ec24124925 WW-4210 Updates tests to use the class definition instead of string 2017-04-20 08:29:53 +02:00
Lukasz Lenart 3e76564d3c WW-4210 Supports defining converter as a class in the annotation 2017-04-20 08:29:24 +02:00
Lukasz Lenart 47552bdb06 WW-4210 Extends annotation to allow use class instead of string 2017-04-20 08:28:46 +02:00
Lukasz Lenart eb4fcb4afc WW-3952 Updates test to include credit card validation 2017-04-19 18:07:38 +02:00
Lukasz Lenart 0813eaa0c8 WW-3952 Extends existing logic to handle credit card validation 2017-04-19 18:07:16 +02:00
Lukasz Lenart 743d27f841 WW-3952 Introduces annotation 2017-04-19 18:06:31 +02:00
Lukasz Lenart fc497360be WW-3952 Adds test to check implementation 2017-04-19 18:06:17 +02:00
Lukasz Lenart 01d710bdf3 WW-3952 Adds implementation of credit card validator 2017-04-19 18:05:53 +02:00
Lukasz Lenart 293d2fd177 WW-4754 Marks sitegraph plugin as deprecated 2017-04-19 10:40:43 +02:00
Lukasz Lenart 5fd605eb97 WW-4578 Supports collections when validating a value 2017-04-19 09:42:06 +02:00
Lukasz Lenart 9d4aac0589 WW-4786 Properly handles empty spaces in path 2017-04-19 09:40:10 +02:00
Lukasz Lenart 13f49a0978 WW-4785 Allows disable multipart support via an config option 2017-04-16 14:33:15 +02:00
Lukasz Lenart 30ec6f8310 Puts back public scope for class 2017-04-14 12:54:20 +02:00
Lukasz Lenart 034e098184 Reduces scope 2017-04-14 12:45:56 +02:00
Lukasz Lenart b1f9068f2b Puts back indentation 2017-04-14 12:25:04 +02:00
Lukasz Lenart 1ad6d2c317 WW-4578 Cleans up code 2017-04-14 11:25:22 +02:00
Lukasz Lenart 569e1f6c82 WW-4578 Fixes small typo 2017-04-14 11:18:39 +02:00
Lukasz Lenart 6f272e4873 WW-4578 Converts URL validator to support collections 2017-04-14 11:16:59 +02:00
Lukasz Lenart 2fe050578a Fixes wrong injection type 2017-04-13 07:31:16 +02:00
Lukasz Lenart 16e8f10f66 WW-4578 Converts length string validator to support collections 2017-04-11 09:10:19 +02:00
Lukasz Lenart df5583f117 WW-4578 Converts required string validator to support collections 2017-04-11 08:46:24 +02:00
Lukasz Lenart 01a56ca117 WW-4578 Converts double range validator to support collections 2017-04-10 21:54:03 +02:00
Lukasz Lenart e66fd538b5 WW-4578 Makes required validator to support collections 2017-04-10 09:13:14 +02:00
Lukasz Lenart 158eda9783 WW-4578 Changes range validators to support collections 2017-04-10 08:37:22 +02:00
Lukasz Lenart caca3d057d WW-4578 Makes RegexFieldValidator to support collections 2017-04-06 08:59:11 +02:00
Lukasz Lenart 1162ddf823 WW-4663 Fallback to old behaviour if there is no action defined 2017-04-04 09:16:48 +02:00
Lukasz Lenart c56d95d28c WW-4779 Marks as deprecated the profiling layer 2017-04-04 08:34:39 +02:00
Lukasz Lenart b4f8d03910 WW-4769 Uses emptySet instead of manually creating Sets 2017-04-04 08:28:04 +02:00
Lukasz Lenart ed20ccd3f3 WW-4744 Solves problem with supporting non public annotated methods in AnnotationWorkflowInterceptor 2017-03-29 07:22:55 +02:00
Yasser Zamani 3cee495a12 WW-4744: Add new line at end of DummyInterface file 2017-03-27 02:10:53 +04:30
Yasser Zamani f0a9226ffb WW-4744: Correct copyright to exactly as previous with no change 2017-03-27 01:53:29 +04:30
Yasser Zamani 67069a9724 WW-4744: Add @Deprecated tags 2017-03-27 01:46:48 +04:30
Yasser Zamani 2d5f0aaa66 WW-4744: Remove copied Spring's code (also from PR#117) 2017-03-26 18:07:58 +04:30
Lukasz Lenart 7627b83555 Moves documentation to wiki 2017-03-24 10:34:39 +01:00
Lukasz Lenart 350c77aba8 WW-4756 Implements TextProviderFactory and used it accros the framework 2017-03-24 10:09:46 +01:00
Lukasz Lenart 3d3bee0218 Process default conversion as required 2017-03-23 15:28:58 +01:00
Lukasz Lenart 98467c2753 Adds missing headers with license 2017-03-23 15:27:55 +01:00
Lukasz Lenart 748da3f8ce WW-4769 Makes excluded classes & packages definitions immutable 2017-03-23 14:50:05 +01:00
Lukasz Lenart d053df4924 WW-4768 Adds support for dashes in boundary definition 2017-03-23 10:13:59 +01:00
Lukasz Lenart 4e9fa84239 WW-4768 Adds proper validation if request is a multipart request 2017-03-23 08:07:21 +01:00
Lukasz Lenart 82f61666f8 WW-4767 Makes some methods protected instead private to allow override them when subclassing 2017-03-23 07:14:32 +01:00
Yasser Zamani e1bb1a70c0 WW-4744: AnnotationUtils supports non-public methods 2017-03-19 20:02:45 +04:30
Lukasz Lenart 915e162e8e Uses protected scope instead of private to allow override when subclassing 2017-03-17 09:25:03 +01:00
Lukasz Lenart e9d7cd09db Increases visibility to allow reuse fields when subclassing 2017-03-17 09:19:23 +01:00
Lukasz Lenart 0378161365 Increases visibility to allow reuse fields when subclassing 2017-03-17 09:09:23 +01:00
Lukasz Lenart 88a39333ce Drops authors tags 2017-03-17 08:12:52 +01:00
Lukasz Lenart e8a598c7bc Updates test to use the new injectable factory 2017-03-17 08:03:48 +01:00
Lukasz Lenart 132829cc24 Defines bean in test configuration 2017-03-17 08:03:28 +01:00
Lukasz Lenart c039685cf9 Moves bean into same area are other releated to it beans 2017-03-17 08:02:52 +01:00
Lukasz Lenart ee6c927db6 Drops unavailable dependency 2017-03-17 08:02:24 +01:00
Lukasz Lenart 944b1a2303 Injects proper dependencies into tag 2017-03-17 07:59:50 +01:00
Lukasz Lenart 16d1d60ed8 Uses dedicated logger instead the one from parent class 2017-03-17 07:51:57 +01:00
Lukasz Lenart 207ac17744 Defines proper interfaces and default implementation in configuration 2017-03-17 07:51:12 +01:00
Lukasz Lenart 3b73544a8f Uses existing instance instead of creating a new one 2017-03-17 07:49:33 +01:00
Aleksandr Mashchenko 6dcff10a68 WW-4761 Improve error logging in DefaultDispatcherErrorHandler 2017-03-16 20:16:06 +02:00
Aleksandr Mashchenko c83e07ec46 WW-4748 Upgrade commons-lang3 to 3.5 2017-03-16 19:53:33 +02:00
Aleksandr Mashchenko d7069fc393 use commons-lang3 classes, use StringUtils removeEnd instead of deprecated chomp 2017-03-16 19:44:15 +02:00
Lukasz Lenart 54974223fa Extracts TextProviderFactory interface 2017-03-16 14:55:35 +01:00
Lukasz Lenart 9376bb0ca1 Renames TextProviderFactory into StrutsTextProviderFactory 2017-03-16 14:43:07 +01:00
Lukasz Lenart 4f46f15184 Adds missing class header with license 2017-03-16 14:39:06 +01:00
Lukasz Lenart 9d60f5bbc6 Adds missing annotation 2017-03-16 08:15:48 +01:00
Lukasz Lenart 4eb22020a4 Decouples LocaleProvider instance from long living interceptor state 2017-03-16 08:15:04 +01:00
Lukasz Lenart 82983713ae Adds info about what other extension point to use 2017-03-15 18:59:55 +01:00
Lukasz Lenart 5452f56648 Adds proper deprecation explanation 2017-03-15 18:58:42 +01:00
Lukasz Lenart c815f5c54d Updates JavaDoc 2017-03-15 18:56:31 +01:00
Lukasz Lenart 47382411d7 Drops unused import 2017-03-15 18:55:26 +01:00
Lukasz Lenart f912dab8f3 Fixes test 2017-03-15 09:18:24 +01:00
Lukasz Lenart 4b022bfbe5 Replaces LocaleProvider with LocaleProviderFactory 2017-03-15 09:11:31 +01:00
Lukasz Lenart 3b738ad3ef Injects factory instead of the provider 2017-03-15 08:53:35 +01:00
Lukasz Lenart 09be1b9b77 Replaces LocaleProvider with LocaleProviderFactory 2017-03-15 08:50:05 +01:00
Lukasz Lenart 636821aa2d Adds default implementation of the factory 2017-03-15 08:46:37 +01:00
Lukasz Lenart b42b9f16c4 Defines new interface for factory 2017-03-15 08:46:09 +01:00
Lukasz Lenart 6ee73dce79 Uses proper setter name 2017-03-14 10:14:03 +01:00
Lukasz Lenart fefb91fe11 Adds info about new extension point 2017-03-14 10:11:23 +01:00
Lukasz Lenart 3a84a626f9 Drops unused function 2017-03-14 10:03:07 +01:00
Lukasz Lenart 008c28ac67 Renames LocalizedTextUtil into DefaultLocalizedTextProvider 2017-03-14 09:50:13 +01:00
Lukasz Lenart c3416e8ea0 Adds missing header 2017-03-14 09:49:54 +01:00
Lukasz Lenart 6e89179eb2 Extracts common interface for LocalizedTextProvider 2017-03-14 09:39:02 +01:00
Lukasz Lenart 31ddb8b602 Fixes small typos 2017-03-13 09:24:55 +01:00
Lukasz Lenart f784a16cb7 Adjusts tests 2017-03-13 09:10:26 +01:00
Lukasz Lenart ca0f62925a Extends constructors to inject LocalizedTextUtil 2017-03-13 09:10:19 +01:00
Lukasz Lenart f1a963b323 Makes class serializable 2017-03-13 09:09:45 +01:00
Lukasz Lenart d1fbf6a897 Merge branch 'injectable-context' into localized-bean 2017-03-13 08:20:42 +01:00
Lukasz Lenart 8852e3d118 Injects missing dependency 2017-03-13 06:56:07 +01:00
Lukasz Lenart 6d60064bea Injects missing dependency 2017-03-13 06:51:47 +01:00
Lukasz Lenart ce2261f6f5 Drops static 2017-03-13 06:47:11 +01:00
Lukasz Lenart 664895a338 Creates dedicated TextProvider for given class 2017-03-13 06:47:03 +01:00
Lukasz Lenart 2b3082ee40 Uses more direct message to link with proper resource bundle 2017-03-13 06:45:37 +01:00
Lukasz Lenart 55ea82cd4f Uses container to inject required dependencies 2017-03-10 09:40:20 +01:00
Lukasz Lenart f56c59c982 Defines TextProviderFactory as a bean 2017-03-10 09:37:36 +01:00
Lukasz Lenart 451eb88307 Adds missing anntoation to properly inject dependency 2017-03-10 09:37:19 +01:00
Lukasz Lenart 57106ccda2 Renames class to match its purpose 2017-03-10 09:21:28 +01:00
Lukasz Lenart c063704d27 Renames class to match its purpose 2017-03-10 09:21:24 +01:00
Lukasz Lenart 3dc8480988 Updates test 2017-03-10 09:20:57 +01:00
Lukasz Lenart d7cf060905 Defines new injectable bean with alias 2017-03-10 09:20:39 +01:00
Lukasz Lenart 3be5aa0fdb Adds missing constructor param 2017-03-10 09:17:25 +01:00
Lukasz Lenart 89d95b64cd Drops empty line 2017-03-10 09:17:03 +01:00
Lukasz Lenart 728055dc46 Adjust usage of the factory 2017-03-10 08:40:24 +01:00
Lukasz Lenart db8ebe710d Uses the new injectable factory to obtain TextProvider 2017-03-10 08:39:55 +01:00
Lukasz Lenart da5da67d80 Makes factory fully injectable 2017-03-10 08:39:20 +01:00
Lukasz Lenart 6c19875c78 Puts back the default bean name to allow alias beans 2017-03-09 10:19:12 +01:00
Lukasz Lenart 1ab616b10b Marks deprecated constructor 2017-03-09 09:44:07 +01:00
Lukasz Lenart 442a52fe48 Marks deprecated constructor 2017-03-09 09:43:38 +01:00
Lukasz Lenart 0b16543d6f Drops support for Xwork xml configuration 2017-03-08 17:52:24 +01:00
Lukasz Lenart 741f3fa107 Marks param as a deprecated 2017-03-08 17:52:01 +01:00
Lukasz Lenart 524343c3cf Moves default bean name to common place 2017-03-08 17:45:32 +01:00
Lukasz Lenart 2733f158d8 Updates test 2017-03-08 17:43:28 +01:00
Lukasz Lenart 4ef6418505 Injects instead of static reference 2017-03-08 17:43:20 +01:00
Lukasz Lenart 239327ea36 Marks static methods as deprecated 2017-03-08 17:42:42 +01:00
Lukasz Lenart fa1a5f9e69 Adds LocalizedTextUtil as a bean 2017-03-08 17:42:13 +01:00
Lukasz Lenart ae09c6a087 Defines LocalizedTextUtil as a bean 2017-03-08 17:41:42 +01:00
Lukasz Lenart b3b8b909e3 Converts into a bean 2017-03-07 11:26:22 +01:00
Lukasz Lenart 6b8272ce47 Uses default error key if specified key doesn't exist 2017-03-06 14:45:26 +01:00
Aleksandr Mashchenko f487d7e7d6 WW-4747 s:file generates input tag with "value" attribute 2017-02-27 22:42:46 +02:00
cnenning 26fa06b117 WW-4741 Merges #119 which avoids session creation on locale read operation 2017-02-27 08:56:31 +01:00
Yasser Zamani 78db281cd6 [WW-4741] Do not force session creation on locale read operation 2017-02-24 18:18:53 +03:30
Aleksandr Mashchenko 38409e0e08 WW-4746 cssErrorClass attribute has no effect on label tag 2017-02-23 19:36:42 +02:00
cnenning c8630ac56d fixed JS to show validation errors as server is now sending status code 400 2017-02-21 09:47:59 +01:00
Lukasz Lenart 9fcf2dfca7 WW-4528 Supports includes/excludes as lists 2017-02-14 08:06:49 +01:00
Yasser Zamani 805cdd7754 Add an Unit test to check if same issue as [WW-4528] has occurred 2017-02-14 01:35:46 +03:30
Aleksandr Mashchenko a399932ae9 WW-4743 NPE in StrutsTilesContainerFactory when resource isn't found 2017-02-13 20:15:18 +02:00
Lukasz Lenart f22873e127 Drops code duplication 2017-02-13 12:00:59 +01:00
Lukasz Lenart 3dafd2a356 Reformats code 2017-02-13 11:58:57 +01:00
Lukasz Lenart 534dfc6bd8 WW-4694 WW-4472 Imroves annotation processing 2017-02-13 11:55:11 +01:00
Lukasz Lenart 4b96958be9 WW-4737 Fixes issue with array of nulls 2017-02-13 10:48:52 +01:00
Yasser Zamani 301844bbaa [WW-4472] using improved AnnotationUtils to navigate around proxies 2017-02-08 00:07:13 +03:30
Yasser Zamani c84b7967e9 [WW-4694] annotation processing improved in order to navigate around
proxies, superclasses and interfaces
2017-02-05 17:54:28 +03:30
Yasser Zamani 51c0bcbb8b [WW-4528] improving testTwoExcludesPropertiesChained unit test 2017-02-03 11:25:02 +03:30
Yasser Zamani 7140e0192b [WW-4528] correcting testTwoExcludesPropertiesChained unit test 2017-02-03 02:37:17 +03:30
Yasser Zamani 0437efc6f9 [WW-4528] handling ChainingInterceptor excludes and includes lists as
comma separated String like ParameterFilterInterceptor do
2017-02-03 02:01:50 +03:30
Aleksandr Mashchenko 533b236fdb WW-4739 <s:reset> tag does not properly interpret the attribute tabindex 2017-02-02 21:56:53 +02:00
Lukasz Lenart ad367777cb Excludes dedicated ext packages 2017-02-02 11:56:30 +01:00
Lukasz Lenart 637ad1c370 Narrows class resolver 2017-02-02 11:37:29 +01:00
Lukasz Lenart 1a8111e0ea Adds additional packages to be excluded from evaluation 2017-02-02 11:37:05 +01:00
antuarc 26bc9cd64b [WW-4737] add unit testing for parameters being converted to string arrays 2017-02-01 12:27:02 +10:00
antuarc 58667e6497 [WW-4737] preserve nulls instead of converting them to the string 'null'
- Also simplify if-else logic to be more readable and avoid double-negatives
2017-02-01 12:26:31 +10:00
Lukasz Lenart 47a415ac5c WW-4736 Upgrades to Log4j2 version 2.8 2017-01-28 09:33:45 +01:00
Lukasz Lenart 465a15664f [maven-release-plugin] prepare for next development iteration 2017-01-27 10:35:30 +01:00
Lukasz Lenart f0f4e9ece7 [maven-release-plugin] prepare release STRUTS_2_5_10 2017-01-27 10:35:05 +01:00
Lukasz Lenart 45580718ab WW-4735 Uses more flexible RegExp to match domain suffixes 2017-01-27 08:27:50 +01:00
Lukasz Lenart 87f113ebb2 Moves description to wiki 2017-01-19 08:36:26 +01:00
Lukasz Lenart 0023d9664b [maven-release-plugin] prepare for next development iteration 2017-01-17 10:27:23 +01:00
Lukasz Lenart ee27b6604a [maven-release-plugin] prepare release STRUTS_2_5_9 2017-01-17 10:27:01 +01:00
Lukasz Lenart 7f80ef1bb5 WW-4734 Fixes proper lookup flow 2017-01-15 17:59:41 +01:00
Lukasz Lenart 253799c6c5 Adds additional excluded packages 2017-01-12 08:07:12 +01:00
Lukasz Lenart 20c54df03a WW-4733 Reduces noise in the logs 2017-01-12 08:06:59 +01:00
Lukasz Lenart ff8d731594 Fixes test to match fixed error message 2017-01-10 19:41:59 +01:00
Lukasz Lenart 880c4c2d33 Fixes small typo 2017-01-10 19:07:37 +01:00
Lukasz Lenart d12bb7cddb WW-4722 Uses proper read method 2017-01-10 19:06:15 +01:00
Lukasz Lenart f48c9620fc WW-4731 Adds additional test case to confirm that everything is ok 2017-01-10 10:28:54 +01:00
Lukasz Lenart fc6ffba9cf WW-4730 Uses session.getId().intern() to properly lock down session 2017-01-09 11:52:30 +01:00
Lukasz Lenart 08e181a4fe WW-4728 Allows override request parameter names used to enable validation 2017-01-09 11:40:13 +01:00
Lukasz Lenart a7f4e255b0 Uses AbstractInterceptor instead of leaving init() and destroy() empty 2017-01-09 10:18:59 +01:00
Lukasz Lenart 2b12f06b01 WW-4687 Allows define interceptors with dynamic parameters 2017-01-09 10:07:20 +01:00
Lukasz Lenart b9c05a7181 Extracts default loop count constant 2017-01-09 09:12:35 +01:00
Lukasz Lenart 14d56fbb93 WW-4731 Moves detailed description to wiki 2017-01-08 20:29:58 +01:00
Lukasz Lenart 97419283ea WW-4050 Converts NoSuchMethodExceotion into ConfigurationException 2017-01-02 09:00:21 +01:00
Lukasz Lenart 55fed53764 WW-4174 Makes fields protected to allow easily override 2017-01-02 08:20:14 +01:00
Lukasz Lenart 98979423a1 WW-3787 WW-3961 Allows include config files from JARs 2016-12-31 12:04:35 +01:00
Lukasz Lenart 3144b6c995 WW-4729 Fixes issue with passing params in location 2016-12-30 14:39:07 +01:00
Lukasz Lenart 3ce21403ee WW-4102 Reduces LOG visibility 2016-12-30 14:11:16 +01:00
Lukasz Lenart e38a71365a WW-4695 Puts back commons-collections 3.x to override Velocity's transitive dependency 2016-12-30 08:06:06 +01:00
Lukasz Lenart f78d90ee22 WW-4726 Fixes wrong value type used to limit upload 2016-12-20 19:50:43 +01:00
Lukasz Lenart 7e4d1b45ea WW-4725 Allows handle all redirection by MessageStoreInterceptor 2016-12-19 18:51:04 +01:00
Lukasz Lenart 4727265e59 [maven-release-plugin] prepare for next development iteration 2016-12-06 11:18:07 +01:00
296 changed files with 6590 additions and 3214 deletions
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.8</version>
<version>2.5.11</version>
</parent>
<artifactId>struts2-apps</artifactId>
<packaging>pom</packaging>
+2 -2
View File
@@ -26,12 +26,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.5.8</version>
<version>2.5.11</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>2.5.8</version>
<version>2.5.11</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.5.8</version>
<version>2.5.11</version>
</parent>
<artifactId>struts2-showcase</artifactId>
@@ -24,23 +24,17 @@ package org.apache.struts2.showcase.chat;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.struts2.dispatcher.SessionMap;
public class ChatAuthenticationInterceptor implements Interceptor {
public class ChatAuthenticationInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LogManager.getLogger(ChatAuthenticationInterceptor.class);
public static final String USER_SESSION_KEY = "chatUserSessionKey";
public void destroy() {
}
public void init() {
}
public String intercept(ActionInvocation invocation) throws Exception {
LOG.debug("Authenticating chat user");
@@ -23,7 +23,7 @@ package org.apache.struts2.showcase.chat;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
@@ -32,7 +32,7 @@ import javax.servlet.http.HttpSession;
/**
* Authenticate showcase chat example, make sure everyone have a username.
*/
public class ChatInterceptor implements Interceptor {
public class ChatInterceptor extends AbstractInterceptor {
private static final Logger LOG = LogManager.getLogger(ChatInterceptor.class);
@@ -40,12 +40,6 @@ public class ChatInterceptor implements Interceptor {
public static final String CHAT_USER_SESSION_KEY = "ChatUserSessionKey";
public void destroy() {
}
public void init() {
}
public String intercept(ActionInvocation invocation) throws Exception {
HttpSession session = (HttpSession) ActionContext.getContext().get(ActionContext.SESSION);
User chatUser = (User) session.getAttribute(CHAT_USER_SESSION_KEY);
@@ -112,8 +112,13 @@ function ajaxFormValidation(event) {
if (restoreFunction) {
restoreFunction();
}
// show user an error message
_handleValidationResult(_form, {errors: ['Network or server error!']})
// struts sends status code 400 when validation errors are present
if (xhr.status === 400) {
_handleValidationResult(_form, JSON.parse(xhr.responseText))
} else {
// a real error occurred -> show user an error message
_handleValidationResult(_form, {errors: ['Network or server error!']})
}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.8</version>
<version>2.5.11</version>
</parent>
<artifactId>struts2-assembly</artifactId>
+3 -3
View File
@@ -10,7 +10,7 @@
</parent>
<artifactId>struts2-bom</artifactId>
<version>2.5.8</version>
<version>2.5.11</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -25,7 +25,7 @@
</licenses>
<properties>
<struts-version.version>2.5.8</struts-version.version>
<struts-version.version>2.5.11</struts-version.version>
</properties>
<build>
@@ -172,6 +172,6 @@
</dependencyManagement>
<scm>
<tag>STRUTS_2_5_8</tag>
<tag>STRUTS_2_5_11</tag>
</scm>
</project>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>2.5.8</version>
<version>2.5.11</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>2.5.8</version>
<version>2.5.11</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.8</version>
<version>2.5.11</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.8</version>
<version>2.5.11</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -19,24 +19,20 @@ import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.ValidationAware;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.Serializable;
import java.util.*;
/**
* Provides a default implementation for the most common actions.
* See the documentation for all the interfaces this class implements for more detailed information.
*/
public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable {
protected static Logger LOG = LogManager.getLogger(ActionSupport.class);
private final ValidationAwareSupport validationAware = new ValidationAwareSupport();
private transient TextProvider textProvider;
private transient LocaleProvider localeProvider;
protected Container container;
@@ -66,17 +62,17 @@ public class ActionSupport implements Action, Validateable, ValidationAware, Tex
@Override
public Locale getLocale() {
return container.getInstance(LocaleProvider.class).getLocale();
return getLocaleProvider().getLocale();
}
@Override
public boolean isValidLocaleString(String localeStr) {
return container.getInstance(LocaleProvider.class).isValidLocaleString(localeStr);
return getLocaleProvider().isValidLocaleString(localeStr);
}
@Override
public boolean isValidLocale(Locale locale) {
return container.getInstance(LocaleProvider.class).isValidLocale(locale);
return getLocaleProvider().isValidLocale(locale);
}
public boolean hasKey(String key) {
@@ -277,17 +273,22 @@ public class ActionSupport implements Action, Validateable, ValidationAware, Tex
*
* @return reference to field with TextProvider
*/
private TextProvider getTextProvider() {
protected TextProvider getTextProvider() {
if (textProvider == null) {
TextProviderFactory tpf = new TextProviderFactory();
if (container != null) {
container.inject(tpf);
}
textProvider = tpf.createInstance(getClass(), this);
TextProviderFactory tpf = container.getInstance(TextProviderFactory.class);
textProvider = tpf.createInstance(getClass());
}
return textProvider;
}
protected LocaleProvider getLocaleProvider() {
if (localeProvider == null) {
LocaleProviderFactory localeProviderFactory = container.getInstance(LocaleProviderFactory.class);
localeProvider = localeProviderFactory.createLocaleProvider();
}
return localeProvider;
}
@Inject
public void setContainer(Container container) {
this.container = container;
@@ -21,7 +21,9 @@ import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.interceptor.WithLazyParams;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
@@ -67,6 +69,7 @@ public class DefaultActionInvocation implements ActionInvocation {
protected Container container;
protected UnknownHandlerManager unknownHandlerManager;
protected OgnlUtil ognlUtil;
protected WithLazyParams.LazyParamInjector lazyParamInjector;
public DefaultActionInvocation(final Map<String, Object> extraContext, final boolean pushAction) {
this.extraContext = extraContext;
@@ -233,11 +236,15 @@ public class DefaultActionInvocation implements ActionInvocation {
}
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
final InterceptorMapping interceptorMapping = interceptors.next();
String interceptorMsg = "interceptorMapping: " + interceptorMapping.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
Interceptor interceptor = interceptorMapping.getInterceptor();
if (interceptor instanceof WithLazyParams) {
interceptor = lazyParamInjector.injectParams(interceptor, interceptorMapping.getParams(), invocationContext);
}
resultCode = interceptor.intercept(DefaultActionInvocation.this);
} finally {
UtilTimerStack.pop(interceptorMsg);
}
@@ -400,6 +407,13 @@ public class DefaultActionInvocation implements ActionInvocation {
invocationContext.setName(proxy.getActionName());
createInterceptors(proxy);
prepareLazyParamInjector(invocationContext.getValueStack());
}
protected void prepareLazyParamInjector(ValueStack valueStack) {
lazyParamInjector = new WithLazyParams.LazyParamInjector(valueStack);
container.inject(lazyParamInjector);
}
protected void createInterceptors(ActionProxy proxy) {
@@ -24,7 +24,6 @@ import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
@@ -53,6 +52,8 @@ public class DefaultActionProxy implements ActionProxy, Serializable {
protected ActionConfig config;
protected ActionInvocation invocation;
protected UnknownHandlerManager unknownHandlerManager;
protected LocalizedTextProvider localizedTextProvider;
protected String actionName;
protected String namespace;
protected String method;
@@ -113,6 +114,11 @@ public class DefaultActionProxy implements ActionProxy, Serializable {
this.actionEventListener = listener;
}
@Inject
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
this.localizedTextProvider = localizedTextProvider;
}
public Object getAction() {
return invocation.getAction();
}
@@ -205,7 +211,7 @@ public class DefaultActionProxy implements ActionProxy, Serializable {
}
protected String prepareNotAllowedErrorMessage() {
return LocalizedTextUtil.findDefaultText(
return localizedTextProvider.findDefaultText(
"struts.exception.method-not-allowed",
Locale.getDefault(),
new String[]{method, actionName}
@@ -214,12 +220,12 @@ public class DefaultActionProxy implements ActionProxy, Serializable {
protected String getErrorMessage() {
if ((namespace != null) && (namespace.trim().length() > 0)) {
return LocalizedTextUtil.findDefaultText(
return localizedTextProvider.findDefaultText(
"xwork.exception.missing-package-action",
Locale.getDefault(),
new String[]{namespace, actionName});
} else {
return LocalizedTextUtil.findDefaultText(
return localizedTextProvider.findDefaultText(
"xwork.exception.missing-action",
Locale.getDefault(),
new String[]{actionName});
@@ -1,8 +1,24 @@
/*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* 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.
*/
package com.opensymphony.xwork2;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import java.util.Arrays;
import java.util.Locale;
@@ -14,6 +30,7 @@ public class DefaultLocaleProvider implements LocaleProvider {
private final static Logger LOG = LogManager.getLogger(DefaultLocaleProvider.class);
@Override
public Locale getLocale() {
ActionContext ctx = ActionContext.getContext();
if (ctx != null) {
@@ -26,7 +43,13 @@ public class DefaultLocaleProvider implements LocaleProvider {
@Override
public boolean isValidLocaleString(String localeStr) {
return isValidLocale(LocalizedTextUtil.localeFromString(localeStr, getLocale()));
Locale locale = null;
try {
locale = LocaleUtils.toLocale(localeStr);
} catch (IllegalArgumentException e) {
LOG.warn(new ParameterizedMessage("Cannot convert [{}] to proper locale", localeStr, e));
}
return isValidLocale(locale);
}
@Override
@@ -0,0 +1,10 @@
package com.opensymphony.xwork2;
public class DefaultLocaleProviderFactory implements LocaleProviderFactory {
@Override
public LocaleProvider createLocaleProvider() {
return new DefaultLocaleProvider();
}
}
@@ -15,7 +15,7 @@
*/
package com.opensymphony.xwork2;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import java.io.Serializable;
@@ -26,26 +26,28 @@ import java.util.List;
import java.util.ResourceBundle;
/**
* DefaultTextProvider gets texts from only the default resource bundles associated with the
* LocalizedTextUtil.
*
* @author Jason Carreira jcarreira@gmail.com
* @author Rainer Hermanns
* @see LocalizedTextUtil#addDefaultResourceBundle(String)
* DefaultTextProvider gets texts from only the default resource bundles associated with the default bundles.
*/
public class DefaultTextProvider implements TextProvider, Serializable, Unchainable {
private static final Object[] EMPTY_ARGS = new Object[0];
protected LocalizedTextProvider localizedTextProvider;
public DefaultTextProvider() {
}
@Inject
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
this.localizedTextProvider = localizedTextProvider;
}
public boolean hasKey(String key) {
return getText(key) != null;
}
public String getText(String key) {
return LocalizedTextUtil.findDefaultText(key, ActionContext.getContext().getLocale());
return localizedTextProvider.findDefaultText(key, ActionContext.getContext().getLocale());
}
public String getText(String key, String defaultValue) {
@@ -64,7 +66,7 @@ public class DefaultTextProvider implements TextProvider, Serializable, Unchaina
params = EMPTY_ARGS;
}
return LocalizedTextUtil.findDefaultText(key, ActionContext.getContext().getLocale(), params);
return localizedTextProvider.findDefaultText(key, ActionContext.getContext().getLocale(), params);
}
public String getText(String key, String[] args) {
@@ -75,7 +77,7 @@ public class DefaultTextProvider implements TextProvider, Serializable, Unchaina
params = EMPTY_ARGS;
}
return LocalizedTextUtil.findDefaultText(key, ActionContext.getContext().getLocale(), params);
return localizedTextProvider.findDefaultText(key, ActionContext.getContext().getLocale(), params);
}
public String getText(String key, String defaultValue, List<?> args) {
@@ -136,7 +138,7 @@ public class DefaultTextProvider implements TextProvider, Serializable, Unchaina
}
public ResourceBundle getTexts(String bundleName) {
return LocalizedTextUtil.findResourceBundle(bundleName, ActionContext.getContext().getLocale());
return localizedTextProvider.findResourceBundle(bundleName, ActionContext.getContext().getLocale());
}
public ResourceBundle getTexts() {
@@ -13,7 +13,7 @@ public interface FileManager {
/**
* Enables configs reloading when config file changed
*
* @param reloadingConfigs {@link XWorkConstants#RELOAD_XML_CONFIGURATION}
* @param reloadingConfigs {@link org.apache.struts2.StrutsConstants#STRUTS_CONFIGURATION_XML_RELOAD}
*/
void setReloadingConfigs(boolean reloadingConfigs);
@@ -0,0 +1,32 @@
/*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* 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.
*/
package com.opensymphony.xwork2;
/**
* Allows delegate creation of {@link LocaleProvider} to another implementation provided
* by a user. It also allows avoid problems with too many dependencies as {@link LocaleProvider}
* is implemented by the {@link ActionSupport} which can be defined as a bean in Spring.
*/
public interface LocaleProviderFactory {
/**
* Create a new instance of {@link LocaleProvider}.
*
* @return the localeProvider.
*/
LocaleProvider createLocaleProvider();
}
@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package com.opensymphony.xwork2;
import com.opensymphony.xwork2.util.ValueStack;
import java.io.Serializable;
import java.util.Locale;
import java.util.ResourceBundle;
public interface LocalizedTextProvider extends Serializable {
String findDefaultText(String aTextName, Locale locale);
String findDefaultText(String aTextName, Locale locale, Object[] params);
ResourceBundle findResourceBundle(String aBundleName, Locale locale);
String findText(Class aClass, String aTextName, Locale locale);
String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args);
String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack);
String findText(ResourceBundle bundle, String aTextName, Locale locale);
String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args);
String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack);
void addDefaultResourceBundle(String resourceBundleName);
}
@@ -0,0 +1,68 @@
/*
* Copyright 2002-2007,2009 The Apache Software Foundation.
*
* 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.
*/
package com.opensymphony.xwork2;
import com.opensymphony.xwork2.inject.Inject;
import java.util.ResourceBundle;
/**
* This factory enables users to provide and correctly initialize a custom TextProvider.
*/
public class StrutsTextProviderFactory implements TextProviderFactory {
protected LocaleProviderFactory localeProviderFactory;
protected LocalizedTextProvider localizedTextProvider;
@Inject
public void setLocaleProviderFactory(LocaleProviderFactory localeProviderFactory) {
this.localeProviderFactory = localeProviderFactory;
}
@Inject
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
this.localizedTextProvider = localizedTextProvider;
}
@Override
public TextProvider createInstance(Class clazz) {
TextProvider instance = getTextProvider(clazz);
if (instance instanceof ResourceBundleTextProvider) {
((ResourceBundleTextProvider) instance).setClazz(clazz);
((ResourceBundleTextProvider) instance).setLocaleProvider(localeProviderFactory.createLocaleProvider());
}
return instance;
}
@Override
public TextProvider createInstance(ResourceBundle bundle) {
TextProvider instance = getTextProvider(bundle);
if (instance instanceof ResourceBundleTextProvider) {
((ResourceBundleTextProvider) instance).setBundle(bundle);
((ResourceBundleTextProvider) instance).setLocaleProvider(localeProviderFactory.createLocaleProvider());
}
return instance;
}
protected TextProvider getTextProvider(Class clazz) {
return new TextProviderSupport(clazz, localeProviderFactory.createLocaleProvider(), localizedTextProvider);
}
protected TextProvider getTextProvider(ResourceBundle bundle) {
return new TextProviderSupport(bundle, localeProviderFactory.createLocaleProvider(), localizedTextProvider);
}
}
@@ -1,12 +1,12 @@
/*
* Copyright 2002-2007,2009 The Apache Software Foundation.
*
*
* 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.
@@ -15,57 +15,12 @@
*/
package com.opensymphony.xwork2;
import com.opensymphony.xwork2.inject.Inject;
import java.util.ResourceBundle;
/**
* This factory enables users to provide and correctly initialize a custom TextProvider.
*
* @author Oleg Gorobets
* @author Rene Gielen
*/
public class TextProviderFactory {
public interface TextProviderFactory {
private TextProvider textProvider;
TextProvider createInstance(Class clazz);
@Inject
public void setTextProvider(TextProvider textProvider) {
this.textProvider = textProvider;
}
public TextProvider createInstance(Class clazz, LocaleProvider provider) {
TextProvider instance = getTextProvider(clazz, provider);
if (instance instanceof ResourceBundleTextProvider) {
((ResourceBundleTextProvider) instance).setClazz(clazz);
((ResourceBundleTextProvider) instance).setLocaleProvider(provider);
}
return instance;
}
public TextProvider createInstance(ResourceBundle bundle, LocaleProvider provider) {
TextProvider instance = getTextProvider(bundle, provider);
if (instance instanceof ResourceBundleTextProvider) {
((ResourceBundleTextProvider) instance).setBundle(bundle);
((ResourceBundleTextProvider) instance).setLocaleProvider(provider);
}
return instance;
}
protected TextProvider getTextProvider(Class clazz, LocaleProvider provider) {
if (this.textProvider == null) {
return new TextProviderSupport(clazz, provider);
} else {
return textProvider;
}
}
private TextProvider getTextProvider(ResourceBundle bundle, LocaleProvider provider) {
if (this.textProvider == null) {
return new TextProviderSupport(bundle, provider);
} else {
return textProvider;
}
}
TextProvider createInstance(ResourceBundle bundle);
}
@@ -16,7 +16,6 @@
package com.opensymphony.xwork2;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.opensymphony.xwork2.util.ValueStack;
import java.util.*;
@@ -30,15 +29,10 @@ import java.util.*;
*/
public class TextProviderSupport implements ResourceBundleTextProvider {
private Class clazz;
private LocaleProvider localeProvider;
private ResourceBundle bundle;
/**
* Default constructor
*/
public TextProviderSupport() {
}
protected Class clazz;
protected LocaleProvider localeProvider;
protected ResourceBundle bundle;
protected LocalizedTextProvider localizedTextProvider;
/**
* Constructor.
@@ -46,9 +40,10 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
* @param clazz a clazz to use for reading the resource bundle.
* @param provider a locale provider.
*/
public TextProviderSupport(Class clazz, LocaleProvider provider) {
public TextProviderSupport(Class clazz, LocaleProvider provider, LocalizedTextProvider localizedTextProvider) {
this.clazz = clazz;
this.localeProvider = provider;
this.localizedTextProvider = localizedTextProvider;
}
/**
@@ -57,14 +52,16 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
* @param bundle the resource bundle.
* @param provider a locale provider.
*/
public TextProviderSupport(ResourceBundle bundle, LocaleProvider provider) {
public TextProviderSupport(ResourceBundle bundle, LocaleProvider provider, LocalizedTextProvider localizedTextProvider) {
this.bundle = bundle;
this.localeProvider = provider;
this.localizedTextProvider = localizedTextProvider;
}
/**
* @param bundle the resource bundle.
*/
@Override
public void setBundle(ResourceBundle bundle) {
this.bundle = bundle;
}
@@ -72,19 +69,28 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
/**
* @param clazz a clazz to use for reading the resource bundle.
*/
@Override
public void setClazz(Class clazz) {
this.clazz = clazz;
}
/**
* @param localeProvider a locale provider.
*/
@Inject
@Override
public void setLocaleProvider(LocaleProvider localeProvider) {
this.localeProvider = localeProvider;
}
@Inject
public void setLocaleProviderFactory(LocaleProviderFactory localeProviderFactory) {
this.localeProvider = localeProviderFactory.createLocaleProvider();
}
@Inject
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
this.localizedTextProvider = localizedTextProvider;
}
/**
* Checks if a key is available in the resource bundles associated with this action.
@@ -97,9 +103,9 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
public boolean hasKey(String key) {
String message;
if (clazz != null) {
message = LocalizedTextUtil.findText(clazz, key, getLocale(), null, new Object[0] );
message = localizedTextProvider.findText(clazz, key, getLocale(), null, new Object[0] );
} else {
message = LocalizedTextUtil.findText(bundle, key, getLocale(), null, new Object[0]);
message = localizedTextProvider.findText(bundle, key, getLocale(), null, new Object[0]);
}
return message != null;
}
@@ -201,9 +207,9 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
public String getText(String key, String defaultValue, List<?> args) {
Object[] argsArray = ((args != null && !args.equals(Collections.emptyList())) ? args.toArray() : null);
if (clazz != null) {
return LocalizedTextUtil.findText(clazz, key, getLocale(), defaultValue, argsArray);
return localizedTextProvider.findText(clazz, key, getLocale(), defaultValue, argsArray);
} else {
return LocalizedTextUtil.findText(bundle, key, getLocale(), defaultValue, argsArray);
return localizedTextProvider.findText(bundle, key, getLocale(), defaultValue, argsArray);
}
}
@@ -222,9 +228,9 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
*/
public String getText(String key, String defaultValue, String[] args) {
if (clazz != null) {
return LocalizedTextUtil.findText(clazz, key, getLocale(), defaultValue, args);
return localizedTextProvider.findText(clazz, key, getLocale(), defaultValue, args);
} else {
return LocalizedTextUtil.findText(bundle, key, getLocale(), defaultValue, args);
return localizedTextProvider.findText(bundle, key, getLocale(), defaultValue, args);
}
}
@@ -252,9 +258,9 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
locale = getLocale();
}
if (clazz != null) {
return LocalizedTextUtil.findText(clazz, key, locale, defaultValue, argsArray, stack);
return localizedTextProvider.findText(clazz, key, locale, defaultValue, argsArray, stack);
} else {
return LocalizedTextUtil.findText(bundle, key, locale, defaultValue, argsArray, stack);
return localizedTextProvider.findText(bundle, key, locale, defaultValue, argsArray, stack);
}
}
@@ -282,9 +288,9 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
locale = getLocale();
}
if (clazz != null) {
return LocalizedTextUtil.findText(clazz, key, locale, defaultValue, args, stack);
return localizedTextProvider.findText(clazz, key, locale, defaultValue, args, stack);
} else {
return LocalizedTextUtil.findText(bundle, key, locale, defaultValue, args, stack);
return localizedTextProvider.findText(bundle, key, locale, defaultValue, args, stack);
}
}
@@ -304,7 +310,7 @@ public class TextProviderSupport implements ResourceBundleTextProvider {
* @return a resource bundle
*/
public ResourceBundle getTexts(String aBundleName) {
return LocalizedTextUtil.findResourceBundle(aBundleName, getLocale());
return localizedTextProvider.findResourceBundle(aBundleName, getLocale());
}
/**
@@ -1,79 +0,0 @@
/*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* 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.
*/
package com.opensymphony.xwork2;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import java.util.Collections;
import java.util.Map;
/**
* Simple facade to make using XWork standalone easier
*/
public class XWork {
ConfigurationManager configurationManager;
public XWork() {
this(new ConfigurationManager());
}
public XWork(ConfigurationManager mgr) {
this.configurationManager = mgr;
}
public void setLoggerFactory(LoggerFactory factory) {
LoggerFactory.setLoggerFactory(factory);
}
/**
* Executes an action
*
* @param namespace The namespace
* @param name The action name
* @param method The method name
* @throws XWorkException If anything goes wrong
*/
public void executeAction(String namespace, String name, String method) throws XWorkException {
Map<String, Object> extraContext = Collections.emptyMap();
executeAction(namespace, name, method, extraContext);
}
/**
* Executes an action with extra context information
*
* @param namespace The namespace
* @param name The action name
* @param method The method name
* @param extraContext A map of extra context information
* @throws XWorkException If anything goes wrong
*/
public void executeAction(String namespace, String name, String method, Map<String, Object> extraContext) throws XWorkException {
Configuration config = configurationManager.getConfiguration();
try {
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);
proxy.execute();
} catch (Exception e) {
throw new XWorkException(e);
} finally {
ActionContext.setContext(null);
}
}
}
@@ -3,29 +3,46 @@ package com.opensymphony.xwork2;
/**
* Constants used across framework
*/
@Deprecated
public final class XWorkConstants {
@Deprecated
public static final String COLLECTION_CONVERTER = "collectionConverter";
@Deprecated
public static final String DATE_CONVERTER = "dateConverter";
@Deprecated
public static final String NUMBER_CONVERTER = "numberConverter";
@Deprecated
public static final String STRING_CONVERTER = "stringConverter";
@Deprecated
public static final String ARRAY_CONVERTER = "arrayConverter";
@Deprecated
public static final String DEV_MODE = "devMode";
@Deprecated
public static final String LOG_MISSING_PROPERTIES = "logMissingProperties";
@Deprecated
public static final String ENABLE_OGNL_EXPRESSION_CACHE = "enableOGNLExpressionCache";
@Deprecated
public static final String ENABLE_OGNL_EVAL_EXPRESSION = "enableOGNLEvalExpression";
@Deprecated
public static final String RELOAD_XML_CONFIGURATION = "reloadXmlConfiguration";
@Deprecated
public static final String ALLOW_STATIC_METHOD_ACCESS = "allowStaticMethodAccess";
@Deprecated
public static final String XWORK_LOGGER_FACTORY = "xwork.loggerFactory";
@Deprecated
public static final String OGNL_EXCLUDED_CLASSES = "ognlExcludedClasses";
@Deprecated
public static final String OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS = "ognlExcludedPackageNamePatterns";
@Deprecated
public static final String OGNL_EXCLUDED_PACKAGE_NAMES = "ognlExcludedPackageNames";
@Deprecated
public static final String ADDITIONAL_EXCLUDED_PATTERNS = "additionalExcludedPatterns";
@Deprecated
public static final String ADDITIONAL_ACCEPTED_PATTERNS = "additionalAcceptedPatterns";
@Deprecated
public static final String OVERRIDE_EXCLUDED_PATTERNS = "overrideExcludedPatterns";
@Deprecated
public static final String OVERRIDE_ACCEPTED_PATTERNS = "overrideAcceptedPatterns";
}
@@ -47,10 +47,6 @@ public class ConfigurationManager {
private boolean providersChanged = false;
private boolean reloadConfigs = true; // for the first time
public ConfigurationManager() {
this("xwork");
}
public ConfigurationManager(String name) {
this.defaultFrameworkBeanName = name;
}
@@ -19,6 +19,8 @@ package com.opensymphony.xwork2.config.entities;
import com.opensymphony.xwork2.interceptor.Interceptor;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* <code>InterceptorMapping</code>
@@ -30,10 +32,16 @@ public class InterceptorMapping implements Serializable {
private String name;
private Interceptor interceptor;
private final Map<String, String> params;
public InterceptorMapping(String name, Interceptor interceptor) {
this(name, interceptor, new HashMap<String, String>());
}
public InterceptorMapping(String name, Interceptor interceptor, Map<String, String> params) {
this.name = name;
this.interceptor = interceptor;
this.params = params;
}
public String getName() {
@@ -44,6 +52,10 @@ public class InterceptorMapping implements Serializable {
return interceptor;
}
public Map<String, String> getParams() {
return params;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -65,7 +77,7 @@ public class InterceptorMapping implements Serializable {
@Override
public String toString() {
return "InterceptorMapping: [" + name + "] => [" + interceptor.getClass().getName() + ']';
return "InterceptorMapping: [" + name + "] => [" + interceptor.getClass().getName() + "] with params [" + params + "]" ;
}
}
@@ -53,7 +53,6 @@ public class DefaultConfiguration implements Configuration {
protected static final Logger LOG = LogManager.getLogger(DefaultConfiguration.class);
// Programmatic Action Configurations
protected Map<String, PackageConfig> packageContexts = new LinkedHashMap<>();
protected RuntimeConfiguration runtimeConfiguration;
@@ -66,7 +65,7 @@ public class DefaultConfiguration implements Configuration {
ObjectFactory objectFactory;
public DefaultConfiguration() {
this("xwork");
this(Container.DEFAULT_NAME);
}
public DefaultConfiguration(String defaultBeanName) {
@@ -260,9 +259,14 @@ public class DefaultConfiguration implements Configuration {
builder.factory(TypeConverter.class, XWorkConstants.NUMBER_CONVERTER, NumberConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, XWorkConstants.STRING_CONVERTER, StringConverter.class, Scope.SINGLETON);
builder.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON);
builder.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON);
builder.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON);
builder.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON);
builder.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON);
builder.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON);
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
builder.factory(OgnlUtil.class, Scope.SINGLETON);
@@ -270,10 +274,12 @@ public class DefaultConfiguration implements Configuration {
builder.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
builder.constant(XWorkConstants.DEV_MODE, "false");
builder.constant(StrutsConstants.STRUTS_DEVMODE, "false");
builder.constant(XWorkConstants.LOG_MISSING_PROPERTIES, "false");
builder.constant(XWorkConstants.ENABLE_OGNL_EVAL_EXPRESSION, "false");
builder.constant(XWorkConstants.ENABLE_OGNL_EXPRESSION_CACHE, "true");
builder.constant(XWorkConstants.RELOAD_XML_CONFIGURATION, "false");
builder.constant(StrutsConstants.STRUTS_I18N_RELOAD, "false");
return builder.create(true);
}
@@ -25,6 +25,7 @@ import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.util.location.Location;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -43,7 +44,6 @@ public class InterceptorBuilder {
private static final Logger LOG = LogManager.getLogger(InterceptorBuilder.class);
/**
* Builds a list of interceptors referenced by the refName in the supplied PackageConfig (InterceptorMapping object).
*
@@ -67,13 +67,11 @@ public class InterceptorBuilder {
InterceptorConfig config = (InterceptorConfig) referencedConfig;
Interceptor inter;
try {
inter = objectFactory.buildInterceptor(config, refParams);
result.add(new InterceptorMapping(refName, inter));
result.add(new InterceptorMapping(refName, inter, refParams));
} catch (ConfigurationException ex) {
LOG.warn("Unable to load config class {} at {} probably due to a missing jar, which might be fine if you never plan to use the {} interceptor",
config.getClassName(), ex.getLocation(), config.getName());
LOG.error("Unable to load config class {}", config.getClassName(), ex);
LOG.warn(new ParameterizedMessage("Unable to load config class {} at {} probably due to a missing jar, which might be fine if you never plan to use the {} interceptor",
config.getClassName(), ex.getLocation(), config.getName()), ex);
}
} else if (referencedConfig instanceof InterceptorStackConfig) {
@@ -2,6 +2,10 @@ package com.opensymphony.xwork2.config.providers;
import com.opensymphony.xwork2.ActionProxyFactory;
import com.opensymphony.xwork2.DefaultActionProxyFactory;
import com.opensymphony.xwork2.DefaultLocaleProviderFactory;
import com.opensymphony.xwork2.LocaleProviderFactory;
import com.opensymphony.xwork2.StrutsTextProviderFactory;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
@@ -9,16 +13,13 @@ import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
import com.opensymphony.xwork2.DefaultLocaleProvider;
import com.opensymphony.xwork2.DefaultTextProvider;
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.TextProviderSupport;
import com.opensymphony.xwork2.UnknownHandlerManager;
import com.opensymphony.xwork2.XWorkConstants;
import com.opensymphony.xwork2.config.Configuration;
@@ -70,6 +71,8 @@ import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
import com.opensymphony.xwork2.util.OgnlTextParser;
import com.opensymphony.xwork2.util.PatternMatcher;
import com.opensymphony.xwork2.util.TextParser;
@@ -177,9 +180,12 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
.factory(TextProvider.class, TextProviderSupport.class, Scope.SINGLETON)
.factory(LocaleProvider.class, DefaultLocaleProvider.class, Scope.SINGLETON)
.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
.factory(OgnlUtil.class, Scope.SINGLETON)
.factory(CollectionConverter.class, Scope.SINGLETON)
.factory(ArrayConverter.class, Scope.SINGLETON)
@@ -194,6 +200,8 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
;
props.setProperty(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE.toString());
props.setProperty(StrutsConstants.STRUTS_I18N_RELOAD, Boolean.FALSE.toString());
props.setProperty(StrutsConstants.STRUTS_DEVMODE, Boolean.FALSE.toString());
props.setProperty(XWorkConstants.DEV_MODE, Boolean.FALSE.toString());
props.setProperty(XWorkConstants.LOG_MISSING_PROPERTIES, Boolean.FALSE.toString());
props.setProperty(XWorkConstants.ENABLE_OGNL_EXPRESSION_CACHE, Boolean.TRUE.toString());
@@ -757,7 +757,7 @@ public class XmlConfigurationProvider implements ConfigurationProvider {
{
// if <result ...>something</result> then we add a parameter of 'something' as this is the most used result param
if (resultElement.getChildNodes().getLength() >= 1) {
resultParams = new LinkedHashMap<String, String>();
resultParams = new LinkedHashMap<>();
String paramName = config.getDefaultResultParam();
if (paramName != null) {
@@ -775,7 +775,7 @@ public class XmlConfigurationProvider implements ConfigurationProvider {
resultParams.put(paramName, val);
}
} else {
LOG.warn("No default parameter defined for result [{}] of type [{}] ", config.getName(), config.getClassName());
LOG.debug("No default parameter defined for result [{}] of type [{}] ", config.getName(), config.getClassName());
}
}
}
@@ -1,3 +1,18 @@
/*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* 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.
*/
package com.opensymphony.xwork2.conversion;
/**
@@ -14,4 +14,13 @@ public interface TypeConverterCreator {
*/
TypeConverter createTypeConverter(String className) throws Exception;
/**
* Creates {@link TypeConverter} from given class
*
* @param clazz convert class
* @return instance of {@link TypeConverter}
* @throws Exception when cannot create/cast to {@link TypeConverter}
*/
TypeConverter createTypeConverter(Class<?> clazz) throws Exception;
}
@@ -15,6 +15,8 @@
*/
package com.opensymphony.xwork2.conversion.annotations;
import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -77,11 +79,17 @@ import java.lang.annotation.Target;
* </tr>
* <tr>
* <td>converter</td>
* <td>either this or value</td>
* <td>DEPRECATED: either this or value</td>
* <td>&nbsp;</td>
* <td>The class name of the TypeConverter to be used as converter.</td>
* </tr>
* <tr>
* <td>converterClass</td>
* <td>either this or value</td>
* <td>&nbsp;</td>
* <td>The class of the TypeConverter to be used as converter. XWorkBasicConverter by default.</td>
* </tr>
* <tr>
* <td>value</td>
* <td>either converter or this</td>
* <td>&nbsp;</td>
@@ -106,27 +114,27 @@ import java.lang.annotation.Target;
*
* private HashMap keyValues = null;
*
* &#64;TypeConversion(type = ConversionType.APPLICATION, converter = "com.opensymphony.xwork2.util.XWorkBasicConverter")
* &#64;TypeConversion(type = ConversionType.APPLICATION)
* public void setConvertInt( String convertInt ) {
* this.convertInt = convertInt;
* }
*
* &#64;TypeConversion(converter = "com.opensymphony.xwork2.util.XWorkBasicConverter")
* &#64;TypeConversion(converterClass = XWorkBasicConverter.class)
* public void setConvertDouble( String convertDouble ) {
* this.convertDouble = convertDouble;
* }
*
* &#64;TypeConversion(rule = ConversionRule.COLLECTION, converter = "java.util.String")
* &#64;TypeConversion(rule = ConversionRule.COLLECTION, converterClass = String.class)
* public void setUsers( List users ) {
* this.users = users;
* }
*
* &#64;TypeConversion(rule = ConversionRule.MAP, converter = "java.math.BigInteger")
* &#64;TypeConversion(rule = ConversionRule.MAP, converterClass = BigInteger.class)
* public void setKeyValues( HashMap keyValues ) {
* this.keyValues = keyValues;
* }
*
* &#64;TypeConversion(type = ConversionType.APPLICATION, property = "java.util.Date", converter = "com.opensymphony.xwork2.util.XWorkBasicConverter")
* &#64;TypeConversion(type = ConversionType.APPLICATION, property = "java.util.Date", converterClass = XWorkBasicConverter.class)
* public String execute() throws Exception {
* return SUCCESS;
* }
@@ -175,9 +183,20 @@ public @interface TypeConversion {
* Note: This can not be used with ConversionRule.KEY_PROPERTY!
*
* @return class of the TypeConverter to be used as converter
* @deprecated user {@link #converterClass()} instead
*/
@Deprecated
String converter() default "";
/**
* The class of the TypeConverter to be used as converter.
*
* Note: This can not be used with ConversionRule.KEY_PROPERTY!
*
* @return class of the TypeConverter to be used as converter
*/
Class<?> converterClass() default XWorkBasicConverter.class;
/**
* If used with ConversionRule.KEY_PROPERTY specify a value here!
*
@@ -8,6 +8,7 @@ import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
import com.opensymphony.xwork2.conversion.annotations.ConversionType;
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -34,28 +35,43 @@ public class DefaultConversionAnnotationProcessor implements ConversionAnnotatio
}
public void process(Map<String, Object> mapping, TypeConversion tc, String key) {
LOG.debug("TypeConversion [{}] with key: [{}]", tc.converter(), key);
LOG.debug("TypeConversion [{}/{}] with key: [{}]", tc.converter(), tc.converterClass(), key);
if (key == null) {
return;
}
try {
if (tc.type() == ConversionType.APPLICATION) {
converterHolder.addDefaultMapping(key, converterCreator.createTypeConverter(tc.converter()));
if (StringUtils.isNoneEmpty(tc.converter())) {
converterHolder.addDefaultMapping(key, converterCreator.createTypeConverter(tc.converter()));
} else {
converterHolder.addDefaultMapping(key, converterCreator.createTypeConverter(tc.converterClass()));
}
} else {
if (tc.rule() == ConversionRule.KEY_PROPERTY || tc.rule() == ConversionRule.CREATE_IF_NULL) {
mapping.put(key, tc.value());
}
//for properties of classes
else if (tc.rule() != ConversionRule.ELEMENT || tc.rule() == ConversionRule.KEY || tc.rule() == ConversionRule.COLLECTION) {
mapping.put(key, converterCreator.createTypeConverter(tc.converter()));
if (StringUtils.isNoneEmpty(tc.converter())) {
mapping.put(key, converterCreator.createTypeConverter(tc.converter()));
} else {
mapping.put(key, converterCreator.createTypeConverter(tc.converterClass()));
}
}
//for keys of Maps
else if (tc.rule() == ConversionRule.KEY) {
Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
Class<?> converterClass;
if (StringUtils.isNoneEmpty(tc.converter())) {
converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
//check if the converter is a type converter if it is one
//then just put it in the map as is. Otherwise
//put a value in for the type converter of the class
} else {
converterClass = tc.converterClass();
}
LOG.debug("Converter class: [{}]", converterClass);
//check if the converter is a type converter if it is one
//then just put it in the map as is. Otherwise
//put a value in for the type converter of the class
if (converterClass.isAssignableFrom(TypeConverter.class)) {
mapping.put(key, converterCreator.createTypeConverter(tc.converter()));
} else {
@@ -65,7 +81,11 @@ public class DefaultConversionAnnotationProcessor implements ConversionAnnotatio
}
//elements(values) of maps / lists
else {
mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
if (StringUtils.isNoneEmpty(tc.converter())) {
mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
} else {
mapping.put(key, tc.converterClass());
}
}
}
} catch (Exception e) {
@@ -1,3 +1,18 @@
/*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* 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.
*/
package com.opensymphony.xwork2.conversion.impl;
import com.opensymphony.xwork2.XWorkException;
@@ -16,9 +31,6 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
/**
* TODO lukaszlenart: add a comment
*/
public class DefaultConversionPropertiesProcessor implements ConversionPropertiesProcessor {
private static final Logger LOG = LogManager.getLogger(DefaultConversionPropertiesProcessor.class);
@@ -59,11 +71,11 @@ public class DefaultConversionPropertiesProcessor implements ConversionPropertie
String key = (String) entry.getKey();
try {
TypeConverter _typeConverter = converterCreator.createTypeConverter((String) entry.getValue());
TypeConverter typeConverter = converterCreator.createTypeConverter((String) entry.getValue());
if (LOG.isDebugEnabled()) {
LOG.debug("\t{}:{} [treated as TypeConverter {}]", key, entry.getValue(), _typeConverter);
LOG.debug("\t{}:{} [treated as TypeConverter {}]", key, entry.getValue(), typeConverter);
}
converterHolder.addDefaultMapping(key, _typeConverter);
converterHolder.addDefaultMapping(key, typeConverter);
} catch (Exception e) {
LOG.error("Conversion registration error", e);
}
@@ -32,6 +32,7 @@ package com.opensymphony.xwork2.conversion.impl;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.LocaleProviderFactory;
import com.opensymphony.xwork2.conversion.TypeConverter;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
@@ -349,7 +350,8 @@ public abstract class DefaultTypeConverter implements TypeConverter {
locale = (Locale) context.get(ActionContext.LOCALE);
}
if (locale == null) {
locale = container.getInstance(LocaleProvider.class).getLocale();
LocaleProviderFactory localeProviderFactory = container.getInstance(LocaleProviderFactory.class);
locale = localeProviderFactory.createLocaleProvider().getLocale();
}
return locale;
}
@@ -19,8 +19,16 @@ public class DefaultTypeConverterCreator implements TypeConverterCreator {
}
public TypeConverter createTypeConverter(String className) throws Exception {
// type converters are used across users
Object obj = objectFactory.buildBean(className, null);
return getTypeConverter(obj);
}
public TypeConverter createTypeConverter(Class<?> clazz) throws Exception {
Object obj = objectFactory.buildBean(clazz, null);
return getTypeConverter(obj);
}
protected TypeConverter getTypeConverter(Object obj) {
if (obj instanceof TypeConverter) {
return (TypeConverter) obj;
@@ -1,32 +1,43 @@
package com.opensymphony.xwork2.conversion.impl;
import com.opensymphony.xwork2.XWorkException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Member;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.Locale;
import java.util.Map;
public class NumberConverter extends DefaultTypeConverter {
private static final Logger LOG = LogManager.getLogger(NumberConverter.class);
public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType) {
if (value instanceof String) {
String stringValue = String.valueOf(value);
if (toType == BigDecimal.class) {
return new BigDecimal((String) value);
return convertToBigDecimal(context, stringValue);
} else if (toType == BigInteger.class) {
return new BigInteger((String) value);
return new BigInteger(stringValue);
} else if (toType == Double.class || toType == double.class) {
return convertToDouble(context, stringValue);
} else if (toType == Float.class || toType == float.class) {
return convertToFloat(context, stringValue);
} else if (toType.isPrimitive()) {
Object convertedValue = super.convertValue(context, value, toType);
String stringValue = (String) value;
if (!isInRange((Number) convertedValue, stringValue, toType))
throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + convertedValue.getClass().getName());
return convertedValue;
} else {
String stringValue = (String) value;
if (!toType.isPrimitive() && (stringValue == null || stringValue.length() == 0)) {
if (!toType.isPrimitive() && stringValue.isEmpty()) {
return null;
}
NumberFormat numFormat = NumberFormat.getInstance(getLocale(context));
@@ -59,6 +70,97 @@ public class NumberConverter extends DefaultTypeConverter {
return super.convertValue(context, value, toType);
}
protected Object convertToBigDecimal(Map<String, Object> context, String stringValue) {
Locale locale = getLocale(context);
NumberFormat format = getNumberFormat(locale);
if (format instanceof DecimalFormat) {
((DecimalFormat) format).setParseBigDecimal(true);
char separator = ((DecimalFormat) format).getDecimalFormatSymbols().getGroupingSeparator();
stringValue = normalize(stringValue, separator);
}
LOG.debug("Trying to convert a value {} with locale {} to BigDecimal", stringValue, locale);
ParsePosition parsePosition = new ParsePosition(0);
Number number = format.parse(stringValue, parsePosition);
if (parsePosition.getIndex() != stringValue.length()) {
throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position " + parsePosition.getIndex());
}
return number;
}
protected Object convertToDouble(Map<String, Object> context, String stringValue) {
Locale locale = getLocale(context);
NumberFormat format = getNumberFormat(locale);
if (format instanceof DecimalFormat) {
char separator = ((DecimalFormat) format).getDecimalFormatSymbols().getGroupingSeparator();
stringValue = normalize(stringValue, separator);
}
LOG.debug("Trying to convert a value {} with locale {} to Double", stringValue, locale);
ParsePosition parsePosition = new ParsePosition(0);
Number number = format.parse(stringValue, parsePosition);
if (parsePosition.getIndex() != stringValue.length()) {
throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position " + parsePosition.getIndex());
}
if (!isInRange(number, stringValue, Double.class)) {
throw new XWorkException("Overflow or underflow converting: \"" + stringValue + "\" into class " + number.getClass().getName());
}
if (number != null) {
return number.doubleValue();
}
return null;
}
protected Object convertToFloat(Map<String, Object> context, String stringValue) {
Locale locale = getLocale(context);
NumberFormat format = getNumberFormat(locale);
if (format instanceof DecimalFormat) {
char separator = ((DecimalFormat) format).getDecimalFormatSymbols().getGroupingSeparator();
stringValue = normalize(stringValue, separator);
}
LOG.debug("Trying to convert a value {} with locale {} to Float", stringValue, locale);
ParsePosition parsePosition = new ParsePosition(0);
Number number = format.parse(stringValue, parsePosition);
if (parsePosition.getIndex() != stringValue.length()) {
throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position " + parsePosition.getIndex());
}
if (!isInRange(number, stringValue, Float.class)) {
throw new XWorkException("Overflow or underflow converting: \"" + stringValue + "\" into class " + number.getClass().getName());
}
if (number != null) {
return number.floatValue();
}
return null;
}
protected NumberFormat getNumberFormat(Locale locale) {
NumberFormat format = NumberFormat.getNumberInstance(locale);
format.setGroupingUsed(true);
return format;
}
protected String normalize(String strValue, char separator) {
// this is a hack as \160 isn't the same as " " (an empty space)
if (separator == 160) {
strValue = strValue.replaceAll(" ", String.valueOf(separator));
}
return strValue;
}
protected boolean isInRange(Number value, String stringValue, Class toType) {
Number bigValue = null;
Number lowerBound = null;
@@ -103,7 +205,7 @@ public class NumberConverter extends DefaultTypeConverter {
return true;
}
return ((Comparable)bigValue).compareTo(lowerBound) >= 0 && ((Comparable)bigValue).compareTo(upperBound) <= 0;
return ((Comparable) bigValue).compareTo(lowerBound) >= 0 && ((Comparable) bigValue).compareTo(upperBound) <= 0;
}
private boolean isIntegerType(Class type) {
@@ -2,58 +2,46 @@ package com.opensymphony.xwork2.conversion.impl;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Array;
import java.lang.reflect.Member;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class StringConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType) {
String result = null;
String result;
if (value instanceof int[]) {
int[] x = (int[]) value;
List<Integer> intArray = new ArrayList<>(x.length);
if (value.getClass().isArray()) {
int length = Array.getLength(value);
List<String> converted = new ArrayList<>(length);
for (int aX : x) {
intArray.add(Integer.valueOf(aX));
for (int i = 0; i < length; i++) {
Object o = Array.get(value, i);
converted.add(convertToString(getLocale(context), o));
}
result = StringUtils.join(intArray, ", ");
} else if (value instanceof long[]) {
long[] x = (long[]) value;
List<Long> longArray = new ArrayList<>(x.length);
result = StringUtils.join(converted, ", ");
} else if(value.getClass().isAssignableFrom(Collection.class)) {
Collection<?> colValue = (Collection) value;
List<String> converted = new ArrayList<>(colValue.hashCode());
for (long aX : x) {
longArray.add(Long.valueOf(aX));
for (Object o : colValue) {
converted.add(convertToString(getLocale(context), o));
}
result = StringUtils.join(longArray, ", ");
} else if (value instanceof double[]) {
double[] x = (double[]) value;
List<Double> doubleArray = new ArrayList<>(x.length);
for (double aX : x) {
doubleArray.add(new Double(aX));
}
result = StringUtils.join(doubleArray, ", ");
} else if (value instanceof boolean[]) {
boolean[] x = (boolean[]) value;
List<Boolean> booleanArray = new ArrayList<>(x.length);
for (boolean aX : x) {
booleanArray.add(new Boolean(aX));
}
result = StringUtils.join(booleanArray, ", ");
result = StringUtils.join(converted, ", ");
} else if (value instanceof Date) {
DateFormat df = null;
DateFormat df;
if (value instanceof java.sql.Time) {
df = DateFormat.getTimeInstance(DateFormat.MEDIUM, getLocale(context));
} else if (value instanceof java.sql.Timestamp) {
@@ -65,9 +53,24 @@ public class StringConverter extends DefaultTypeConverter {
df = DateFormat.getDateInstance(DateFormat.SHORT, getLocale(context));
}
result = df.format(value);
} else if (value instanceof String[]) {
result = StringUtils.join((String[]) value, ", ");
} else {
result = convertToString(getLocale(context), value);
}
return result;
}
protected String convertToString(Locale locale, Object value) {
if (Number.class.isInstance(value)) {
NumberFormat format = NumberFormat.getNumberInstance(locale);
format.setGroupingUsed(false);
if (Double.class.isInstance(value) || BigDecimal.class.isInstance(value)) {
format.setMinimumFractionDigits(1);
}
return format.format(value);
} else {
return String.valueOf(value);
}
}
}
@@ -173,8 +173,7 @@ public class XWorkConverter extends DefaultTypeConverter {
@Inject
public void setConversionPropertiesProcessor(ConversionPropertiesProcessor propertiesProcessor) {
// note: this file is deprecated
propertiesProcessor.process("xwork-default-conversion.properties");
propertiesProcessor.processRequired("struts-default-conversion.properties");
propertiesProcessor.process("xwork-conversion.properties");
}
@@ -194,7 +193,8 @@ public class XWorkConverter extends DefaultTypeConverter {
}
public static String getConversionErrorMessage(String propertyName, ValueStack stack) {
String defaultMessage = LocalizedTextUtil.findDefaultText("xwork.default.invalid.fieldvalue",
LocalizedTextProvider localizedTextProvider = ActionContext.getContext().getContainer().getInstance(LocalizedTextProvider.class);
String defaultMessage = localizedTextProvider.findDefaultText("xwork.default.invalid.fieldvalue",
ActionContext.getContext().getLocale(),
new Object[]{
propertyName
@@ -487,9 +487,9 @@ public class XWorkConverter extends DefaultTypeConverter {
}
if (LOG.isDebugEnabled()) {
if (StringUtils.isEmpty(tc.key())) {
LOG.debug("WARNING! key of @TypeConversion [{}] applied to [{}] is empty!", tc.converter(), clazz.getName());
LOG.debug("WARNING! key of @TypeConversion [{}/{}] applied to [{}] is empty!", tc.converter(), tc.converterClass(), clazz.getName());
} else {
LOG.debug("TypeConversion [{}] with key: [{}]", tc.converter(), tc.key());
LOG.debug("TypeConversion [{}/{}] with key: [{}]", tc.converter(), tc.converterClass(), tc.key());
}
}
annotationProcessor.process(mapping, tc, tc.key());
@@ -5,7 +5,10 @@ import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.entities.InterceptorConfig;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.WithLazyParams;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
import java.util.Map;
@@ -15,6 +18,8 @@ import java.util.Map;
*/
public class DefaultInterceptorFactory implements InterceptorFactory {
private static final Logger LOG = LogManager.getLogger(DefaultInterceptorFactory.class);
private ObjectFactory objectFactory;
private ReflectionProvider reflectionProvider;
@@ -40,7 +45,12 @@ public class DefaultInterceptorFactory implements InterceptorFactory {
try {
// interceptor instances are long-lived and used across user sessions, so don't try to pass in any extra context
Object o = objectFactory.buildBean(interceptorClassName, null);
reflectionProvider.setProperties(params, o);
if (o instanceof WithLazyParams) {
LOG.debug("Interceptor {} is marked with interface {} and params will be set during action invocation",
interceptorClassName, WithLazyParams.class.getName());
} else {
reflectionProvider.setProperties(params, o);
}
if (o instanceof Interceptor) {
Interceptor interceptor = (Interceptor) o;
@@ -21,6 +21,7 @@ package com.opensymphony.xwork2.inject.util;
*
* @author crazybob@google.com (Bob Lee)
*/
@Deprecated
public class Strings {
/**
@@ -43,6 +44,7 @@ public class Strings {
* converted to uppercase
* @throws NullPointerException if <tt>s</tt> is null
*/
@Deprecated
public static String capitalize(String s) {
if (s.length() == 0)
return s;
@@ -23,7 +23,7 @@ import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClearableValueStack;
import com.opensymphony.xwork2.util.Evaluated;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
@@ -94,11 +94,12 @@ public class AliasInterceptor extends AbstractInterceptor {
protected String aliasesKey = DEFAULT_ALIAS_KEY;
protected ValueStackFactory valueStackFactory;
static boolean devMode = false;
protected LocalizedTextProvider localizedTextProvider;
protected boolean devMode = false;
@Inject(XWorkConstants.DEV_MODE)
public static void setDevMode(String mode) {
devMode = "true".equals(mode);
public void setDevMode(String mode) {
this.devMode = Boolean.parseBoolean(mode);
}
@Inject
@@ -106,6 +107,11 @@ public class AliasInterceptor extends AbstractInterceptor {
this.valueStackFactory = valueStackFactory;
}
@Inject
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
this.localizedTextProvider = localizedTextProvider;
}
/**
* <p>
* Sets the name of the action parameter to look for the alias map.
@@ -173,7 +179,7 @@ public class AliasInterceptor extends AbstractInterceptor {
newStack.setValue(alias, value.get());
} catch (RuntimeException e) {
if (devMode) {
String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
String developerNotification = localizedTextProvider.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
"Unexpected Exception caught setting '" + entry.getKey() + "' on '" + action.getClass() + ": " + e.getMessage()
});
LOG.error(developerNotification);
@@ -20,7 +20,9 @@ import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.Unchainable;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ProxyUtil;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
@@ -160,7 +162,12 @@ public class ChainingInterceptor extends AbstractInterceptor {
Map<String, Object> ctxMap = invocation.getInvocationContext().getContextMap();
for (Object object : list) {
if (shouldCopy(object)) {
reflectionProvider.copy(object, invocation.getAction(), ctxMap, prepareExcludes(), includes);
Object action = invocation.getAction();
Class<?> editable = null;
if(ProxyUtil.isProxy(action)) {
editable = ProxyUtil.ultimateTargetClass(action);
}
reflectionProvider.copy(object, action, ctxMap, prepareExcludes(), includes, editable);
}
}
}
@@ -210,12 +217,21 @@ public class ChainingInterceptor extends AbstractInterceptor {
return excludes;
}
/**
* Sets the list of parameter names to exclude from copying (all others will be included).
*
* @param excludes the excludes list as comma separated String
*/
public void setExcludes(String excludes) {
this.excludes = TextParseUtil.commaDelimitedStringToSet(excludes);
}
/**
* Sets the list of parameter names to exclude from copying (all others will be included).
*
* @param excludes the excludes list
*/
public void setExcludes(Collection<String> excludes) {
public void setExcludesCollection(Collection<String> excludes) {
this.excludes = excludes;
}
@@ -228,12 +244,22 @@ public class ChainingInterceptor extends AbstractInterceptor {
return includes;
}
/**
* Sets the list of parameter names to include when copying (all others will be excluded).
*
* @param includes the includes list as comma separated String
*/
public void setIncludes(String includes) {
this.includes = TextParseUtil.commaDelimitedStringToSet(includes);
}
/**
* Sets the list of parameter names to include when copying (all others will be excluded).
*
* @param includes the includes list
*/
public void setIncludes(Collection<String> includes) {
public void setIncludesCollection(Collection<String> includes) {
this.includes = includes;
}
@@ -19,10 +19,9 @@ import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.annotations.InputConfig;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.opensymphony.xwork2.util.AnnotationUtils;
import java.lang.reflect.Method;
/**
* <!-- START SNIPPET: description -->
@@ -207,11 +206,11 @@ public class DefaultWorkflowInterceptor extends MethodFilterInterceptor {
*/
protected String processInputConfig(final Object action, final String method, final String currentResultName) throws Exception {
String resultName = currentResultName;
InputConfig annotation = action.getClass().getMethod(method, EMPTY_CLASS_ARRAY).getAnnotation(InputConfig.class);
InputConfig annotation = MethodUtils.getAnnotation(action.getClass().getMethod(method, EMPTY_CLASS_ARRAY),
InputConfig.class ,true,true);
if (annotation != null) {
if (StringUtils.isNotEmpty(annotation.methodName())) {
Method m = action.getClass().getMethod(annotation.methodName());
resultName = (String) m.invoke(action);
resultName = (String) MethodUtils.invokeMethod(action, true, annotation.methodName());
} else {
resultName = annotation.resultName();
}
@@ -153,7 +153,7 @@ import java.util.Map;
*/
public class ExceptionMappingInterceptor extends AbstractInterceptor {
protected static final Logger LOG = LogManager.getLogger(ExceptionMappingInterceptor.class);
private static final Logger LOG = LogManager.getLogger(ExceptionMappingInterceptor.class);
protected Logger categoryLogger;
protected boolean logEnabled = false;
@@ -70,7 +70,8 @@ import java.util.Set;
* @see com.opensymphony.xwork2.validator.ValidationInterceptor
*/
public abstract class MethodFilterInterceptor extends AbstractInterceptor {
protected transient Logger log = LogManager.getLogger(getClass());
private static final Logger LOG = LogManager.getLogger(MethodFilterInterceptor.class);
protected Set<String> excludeMethods = Collections.emptySet();
protected Set<String> includeMethods = Collections.emptySet();
@@ -104,7 +105,7 @@ public abstract class MethodFilterInterceptor extends AbstractInterceptor {
// ValidationInterceptor
boolean applyMethod = MethodFilterInterceptorUtil.applyMethod(excludeMethods, includeMethods, method);
if (!applyMethod) {
log.debug("Skipping Interceptor... Method [{}] found in exclude list.", method);
LOG.debug("Skipping Interceptor... Method [{}] found in exclude list.", method);
}
return applyMethod;
}
@@ -17,6 +17,7 @@ package com.opensymphony.xwork2.interceptor;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.XWorkConstants;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
@@ -218,15 +219,19 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
}
protected void notifyDeveloperParameterException(Object action, String property, String message) {
String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification",
ActionContext.getContext().getLocale(), "Developer Notification:\n{0}",
new Object[]{
"Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message
}
);
String developerNotification = "Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message;
if (action instanceof TextProvider) {
TextProvider tp = (TextProvider) action;
developerNotification = tp.getText("devmode.notification",
"Developer Notification:\n{0}",
new String[]{ developerNotification }
);
}
LOG.error(developerNotification);
// see https://issues.apache.org/jira/browse/WW-4066
if (action instanceof ValidationAware) {
// see https://issues.apache.org/jira/browse/WW-4066
Collection<String> messages = ((ValidationAware) action).getActionMessages();
messages.add(message);
((ValidationAware) action).setActionMessages(messages);
@@ -22,7 +22,7 @@ import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.Parameterizable;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClearableValueStack;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
@@ -93,6 +93,7 @@ public class StaticParametersInterceptor extends AbstractInterceptor {
private static final Logger LOG = LogManager.getLogger(StaticParametersInterceptor.class);
private ValueStackFactory valueStackFactory;
private LocalizedTextProvider localizedTextProvider;
@Inject
public void setValueStackFactory(ValueStackFactory valueStackFactory) {
@@ -102,7 +103,12 @@ public class StaticParametersInterceptor extends AbstractInterceptor {
@Inject(XWorkConstants.DEV_MODE)
public void setDevMode(String mode) {
devMode = BooleanUtils.toBoolean(mode);
}
}
@Inject
public void setLocalizedTextProvider(LocalizedTextProvider localizedTextProvider) {
this.localizedTextProvider = localizedTextProvider;
}
public void setParse(String value) {
this.parse = BooleanUtils.toBoolean(value);
@@ -168,7 +174,8 @@ public class StaticParametersInterceptor extends AbstractInterceptor {
newStack.setValue(entry.getKey(), val);
} catch (RuntimeException e) {
if (devMode) {
String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
String developerNotification = localizedTextProvider.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
"Unexpected Exception caught setting '" + entry.getKey() + "' on '" + action.getClass() + ": " + e.getMessage()
});
LOG.error(developerNotification);
@@ -91,9 +91,13 @@ import org.apache.logging.log4j.Logger;
*
* @author Jason Carreira
* @author Claus Ibsen
*
* @deprecated will be dropped with next major release (2.6)
*/
@Deprecated
public class TimerInterceptor extends AbstractInterceptor {
protected static final Logger LOG = LogManager.getLogger(TimerInterceptor.class);
private static final Logger LOG = LogManager.getLogger(TimerInterceptor.class);
protected Logger categoryLogger;
protected String logCategory;
@@ -0,0 +1,63 @@
package com.opensymphony.xwork2.interceptor;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import java.util.Map;
/**
* Interceptors marked with this interface won't be fully initialised during initialisation.
* Appropriated params will be injected just before usage of the interceptor.
*
* Please be aware that in such case {@link Interceptor#init()} method must be prepared for this.
*
* @since 2.5.9
*/
public interface WithLazyParams {
class LazyParamInjector {
protected OgnlUtil ognlUtil;
protected TextParser textParser;
protected ReflectionProvider reflectionProvider;
private final TextParseUtil.ParsedValueEvaluator valueEvaluator;
public LazyParamInjector(final ValueStack valueStack) {
valueEvaluator = new TextParseUtil.ParsedValueEvaluator() {
public Object evaluate(String parsedValue) {
return valueStack.findValue(parsedValue); // no asType !!!
}
};
}
@Inject
public void setTextParser(TextParser textParser) {
this.textParser = textParser;
}
@Inject
public void setReflectionProvider(ReflectionProvider reflectionProvider) {
this.reflectionProvider = reflectionProvider;
}
@Inject
public void setOgnlUtil(OgnlUtil ognlUtil) {
this.ognlUtil = ognlUtil;
}
public Interceptor injectParams(Interceptor interceptor, Map<String, String> params, ActionContext invocationContext) {
for (Map.Entry<String, String> entry : params.entrySet()) {
Object paramValue = textParser.evaluate(new char[]{ '$' }, entry.getValue(), valueEvaluator, TextParser.DEFAULT_LOOP_COUNT);
ognlUtil.setProperty(entry.getKey(), paramValue, interceptor, invocationContext.getContextMap());
}
return interceptor;
}
}
}
@@ -19,7 +19,7 @@ import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.util.AnnotationUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -112,17 +112,19 @@ public class AnnotationWorkflowInterceptor extends AbstractInterceptor implement
public String intercept(ActionInvocation invocation) throws Exception {
final Object action = invocation.getAction();
invocation.addPreResultListener(this);
List<Method> methods = new ArrayList<>(AnnotationUtils.getAnnotatedMethods(action.getClass(), Before.class));
List<Method> methods = new ArrayList<>(MethodUtils.getMethodsListWithAnnotation(action.getClass(), Before.class,
true, true));
if (methods.size() > 0) {
// methods are only sorted by priority
Collections.sort(methods, new Comparator<Method>() {
public int compare(Method method1, Method method2) {
return comparePriorities(method1.getAnnotation(Before.class).priority(),
method2.getAnnotation(Before.class).priority());
return comparePriorities(MethodUtils.getAnnotation(method1, Before.class, true,
true).priority(), MethodUtils.getAnnotation(method2, Before.class, true,
true).priority());
}
});
for (Method m : methods) {
final String resultCode = (String) m.invoke(action, (Object[]) null);
final String resultCode = (String) MethodUtils.invokeMethod(action, true, m.getName());
if (resultCode != null) {
// shortcircuit execution
return resultCode;
@@ -133,18 +135,20 @@ public class AnnotationWorkflowInterceptor extends AbstractInterceptor implement
String invocationResult = invocation.invoke();
// invoke any @After methods
methods = new ArrayList<Method>(AnnotationUtils.getAnnotatedMethods(action.getClass(), After.class));
methods = new ArrayList<Method>(MethodUtils.getMethodsListWithAnnotation(action.getClass(), After.class,
true, true));
if (methods.size() > 0) {
// methods are only sorted by priority
Collections.sort(methods, new Comparator<Method>() {
public int compare(Method method1, Method method2) {
return comparePriorities(method1.getAnnotation(After.class).priority(),
method2.getAnnotation(After.class).priority());
return comparePriorities(MethodUtils.getAnnotation(method1, After.class, true,
true).priority(), MethodUtils.getAnnotation(method2, After.class, true,
true).priority());
}
});
for (Method m : methods) {
m.invoke(action, (Object[]) null);
MethodUtils.invokeMethod(action, true, m.getName());
}
}
@@ -168,19 +172,21 @@ public class AnnotationWorkflowInterceptor extends AbstractInterceptor implement
*/
public void beforeResult(ActionInvocation invocation, String resultCode) {
Object action = invocation.getAction();
List<Method> methods = new ArrayList<Method>(AnnotationUtils.getAnnotatedMethods(action.getClass(), BeforeResult.class));
List<Method> methods = new ArrayList<Method>(MethodUtils.getMethodsListWithAnnotation(action.getClass(),
BeforeResult.class, true, true));
if (methods.size() > 0) {
// methods are only sorted by priority
Collections.sort(methods, new Comparator<Method>() {
public int compare(Method method1, Method method2) {
return comparePriorities(method1.getAnnotation(BeforeResult.class).priority(),
method2.getAnnotation(BeforeResult.class).priority());
return comparePriorities(MethodUtils.getAnnotation(method1, BeforeResult.class, true,
true).priority(), MethodUtils.getAnnotation(method2, BeforeResult.class,
true, true).priority());
}
});
for (Method m : methods) {
try {
m.invoke(action, (Object[]) null);
MethodUtils.invokeMethod(action, true, m.getName());
} catch (Exception e) {
throw new XWorkException(e);
}
@@ -16,16 +16,15 @@
package com.opensymphony.xwork2.mock;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import org.junit.Assert;
/**
* Mock for an {@link com.opensymphony.xwork2.interceptor.Interceptor}.
*
* @author Jason Carreira
*/
public class MockInterceptor implements Interceptor {
public class MockInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 2692551676567227756L;
@@ -57,12 +56,6 @@ public class MockInterceptor implements Interceptor {
return foo;
}
/**
* Called to let an interceptor clean up any resources it has allocated.
*/
public void destroy() {
}
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -101,13 +94,6 @@ public class MockInterceptor implements Interceptor {
return result;
}
/**
* Called after an Interceptor is created, but before any requests are processed using the intercept() methodName. This
* gives the Interceptor a chance to initialize any needed resources.
*/
public void init() {
}
/**
* Allows the Interceptor to do some processing on the request before and/or after the rest of the processing of the
* request by the DefaultActionInvocation or to short-circuit the processing and just return a String return code.
@@ -70,7 +70,12 @@ public class OgnlReflectionProvider implements ReflectionProvider {
public void copy(Object from, Object to, Map<String, Object> context,
Collection<String> exclusions, Collection<String> inclusions) {
ognlUtil.copy(from, to, context, exclusions, inclusions);
copy(from, to, context, exclusions, inclusions, null);
}
public void copy(Object from, Object to, Map<String, Object> context,
Collection<String> exclusions, Collection<String> inclusions, Class<?> editable) {
ognlUtil.copy(from, to, context, exclusions, inclusions, editable);
}
public Object getRealTarget(String property, Map<String, Object> context, Object root)
@@ -28,6 +28,7 @@ import ognl.*;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
@@ -49,6 +50,7 @@ import java.util.regex.Pattern;
public class OgnlUtil {
private static final Logger LOG = LogManager.getLogger(OgnlUtil.class);
private ConcurrentMap<String, Object> expressions = new ConcurrentHashMap<>();
private final ConcurrentMap<Class, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
private TypeConverter defaultConverter;
@@ -57,12 +59,13 @@ public class OgnlUtil {
private boolean enableExpressionCache = true;
private boolean enableEvalExpression;
private Set<Class<?>> excludedClasses = new HashSet<>();
private Set<Pattern> excludedPackageNamePatterns = new HashSet<>();
private Set<String> excludedPackageNames = new HashSet<>();
private Set<Class<?>> excludedClasses = Collections.emptySet();
private Set<Pattern> excludedPackageNamePatterns = Collections.emptySet();
private Set<String> excludedPackageNames = Collections.emptySet();
private Container container;
private boolean allowStaticMethodAccess;
private boolean disallowProxyMemberAccess;
@Inject
public void setXWorkConverter(XWorkConverter conv) {
@@ -90,27 +93,35 @@ public class OgnlUtil {
@Inject(value = XWorkConstants.OGNL_EXCLUDED_CLASSES, required = false)
public void setExcludedClasses(String commaDelimitedClasses) {
Set<String> classes = TextParseUtil.commaDelimitedStringToSet(commaDelimitedClasses);
for (String className : classes) {
Set<String> classNames = TextParseUtil.commaDelimitedStringToSet(commaDelimitedClasses);
Set<Class<?>> classes = new HashSet<>();
for (String className : classNames) {
try {
excludedClasses.add(Class.forName(className));
classes.add(Class.forName(className));
} catch (ClassNotFoundException e) {
throw new ConfigurationException("Cannot load excluded class: " + className, e);
}
}
excludedClasses = Collections.unmodifiableSet(classes);
}
@Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
public void setExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
Set<String> packagePatterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackagePatterns);
Set<Pattern> packageNamePatterns = new HashSet<>();
for (String pattern : packagePatterns) {
excludedPackageNamePatterns.add(Pattern.compile(pattern));
packageNamePatterns.add(Pattern.compile(pattern));
}
excludedPackageNamePatterns = Collections.unmodifiableSet(packageNamePatterns);
}
@Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAMES, required = false)
public void setExcludedPackageNames(String commaDelimitedPackageNames) {
excludedPackageNames = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames);
excludedPackageNames = Collections.unmodifiableSet(TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames));
}
public Set<Class<?>> getExcludedClasses() {
@@ -135,6 +146,15 @@ public class OgnlUtil {
this.allowStaticMethodAccess = Boolean.parseBoolean(allowStaticMethodAccess);
}
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false)
public void setDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
this.disallowProxyMemberAccess = Boolean.parseBoolean(disallowProxyMemberAccess);
}
public boolean isDisallowProxyMemberAccess() {
return disallowProxyMemberAccess;
}
/**
* Sets the object's properties using the default type converter, defaulting to not throw
* exceptions for problems setting the properties.
@@ -441,6 +461,24 @@ public class OgnlUtil {
* note if exclusions AND inclusions are supplied and not null nothing will get copied.
*/
public void copy(final Object from, final Object to, final Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions) {
copy(from, to, context, exclusions, inclusions, null);
}
/**
* Copies the properties in the object "from" and sets them in the object "to"
* only setting properties defined in the given "editable" class (or interface)
* using specified type converter, or {@link com.opensymphony.xwork2.conversion.impl.XWorkConverter} if none
* is specified.
*
* @param from the source object
* @param to the target object
* @param context the action context we're running under
* @param exclusions collection of method names to excluded from copying ( can be null)
* @param inclusions collection of method names to included copying (can be null)
* note if exclusions AND inclusions are supplied and not null nothing will get copied.
* @param editable the class (or interface) to restrict property setting to
*/
public void copy(final Object from, final Object to, final Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions, Class<?> editable) {
if (from == null || to == null) {
LOG.warn("Attempting to copy from or to a null source. This is illegal and is bein skipped. This may be due to an error in an OGNL expression, action chaining, or some other event.");
return;
@@ -457,7 +495,12 @@ public class OgnlUtil {
try {
fromPds = getPropertyDescriptors(from);
toPds = getPropertyDescriptors(to);
if (editable != null) {
toPds = getPropertyDescriptors(editable);
}
else {
toPds = getPropertyDescriptors(to);
}
} catch (IntrospectionException e) {
LOG.error("An error occurred", e);
return;
@@ -647,6 +690,7 @@ public class OgnlUtil {
memberAccess.setExcludedClasses(excludedClasses);
memberAccess.setExcludedPackageNamePatterns(excludedPackageNamePatterns);
memberAccess.setExcludedPackageNames(excludedPackageNames);
memberAccess.setDisallowProxyMemberAccess(disallowProxyMemberAccess);
return Ognl.createDefaultContext(root, resolver, defaultConverter, memberAccess);
}
@@ -52,19 +52,20 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
public static final String THROW_EXCEPTION_ON_FAILURE = OgnlValueStack.class.getName() + ".throwExceptionOnFailure";
private static final Logger LOG = LogManager.getLogger(OgnlValueStack.class);
private static final long serialVersionUID = 370737852934925530L;
private static final String MAP_IDENTIFIER_KEY = "com.opensymphony.xwork2.util.OgnlValueStack.MAP_IDENTIFIER_KEY";
private static final Logger LOG = LogManager.getLogger(OgnlValueStack.class);
CompoundRoot root;
transient Map<String, Object> context;
Class defaultType;
Map<Object, Object> overrides;
transient OgnlUtil ognlUtil;
transient SecurityMemberAccess securityMemberAccess;
protected CompoundRoot root;
protected transient Map<String, Object> context;
protected Class defaultType;
protected Map<Object, Object> overrides;
protected transient OgnlUtil ognlUtil;
protected transient SecurityMemberAccess securityMemberAccess;
private transient XWorkConverter converter;
private boolean devMode;
private boolean logMissingProperties;
@@ -83,6 +84,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
securityMemberAccess.setExcludedClasses(ognlUtil.getExcludedClasses());
securityMemberAccess.setExcludedPackageNamePatterns(ognlUtil.getExcludedPackageNamePatterns());
securityMemberAccess.setExcludedPackageNames(ognlUtil.getExcludedPackageNames());
securityMemberAccess.setDisallowProxyMemberAccess(ognlUtil.isDisallowProxyMemberAccess());
}
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot,
@@ -189,7 +191,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
context.remove(REPORT_ERRORS_ON_NO_PROP);
}
private void handleRuntimeException(String expr, Object value, boolean throwExceptionOnFailure, RuntimeException re) {
protected void handleRuntimeException(String expr, Object value, boolean throwExceptionOnFailure, RuntimeException re) {
if (throwExceptionOnFailure) {
String message = ErrorMessageBuilder.create()
.errorSettingExpressionWithValue(expr, value)
@@ -200,7 +202,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
}
}
private void handleOgnlException(String expr, Object value, boolean throwExceptionOnFailure, OgnlException e) {
protected void handleOgnlException(String expr, Object value, boolean throwExceptionOnFailure, OgnlException e) {
boolean shouldLog = shouldLogMissingPropertyWarning(e);
String msg = null;
if (throwExceptionOnFailure || shouldLog) {
@@ -242,7 +244,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
}
}
private void setupExceptionOnFailure(boolean throwExceptionOnFailure) {
protected void setupExceptionOnFailure(boolean throwExceptionOnFailure) {
if (throwExceptionOnFailure) {
context.put(THROW_EXCEPTION_ON_FAILURE, true);
}
@@ -255,7 +257,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
return tryFindValue(expr);
}
private Object handleOtherException(String expr, boolean throwExceptionOnFailure, Exception e) {
protected Object handleOtherException(String expr, boolean throwExceptionOnFailure, Exception e) {
logLookupFailure(expr, e);
if (throwExceptionOnFailure)
@@ -322,7 +324,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
return tryFindValue(expr, asType);
}
private Object handleOgnlException(String expr, boolean throwExceptionOnFailure, OgnlException e) {
protected Object handleOgnlException(String expr, boolean throwExceptionOnFailure, OgnlException e) {
Object ret = findInContext(expr);
if (ret == null) {
if (shouldLogMissingPropertyWarning(e)) {
@@ -335,7 +337,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
return ret;
}
private boolean shouldLogMissingPropertyWarning(OgnlException e) {
protected boolean shouldLogMissingPropertyWarning(OgnlException e) {
return (e instanceof NoSuchPropertyException || e instanceof MethodFailedException)
&& devMode && logMissingProperties;
}
@@ -359,7 +361,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
return ognlUtil.getValue(expr, context, root, asType);
}
private Object findInContext(String name) {
protected Object findInContext(String name) {
return getContext().get(name);
}
@@ -38,11 +38,11 @@ import java.util.Set;
*/
public class OgnlValueStackFactory implements ValueStackFactory {
private XWorkConverter xworkConverter;
private CompoundRootAccessor compoundRootAccessor;
private TextProvider textProvider;
private Container container;
private boolean allowStaticMethodAccess;
protected XWorkConverter xworkConverter;
protected CompoundRootAccessor compoundRootAccessor;
protected TextProvider textProvider;
protected Container container;
protected boolean allowStaticMethodAccess;
@Inject
public void setXWorkConverter(XWorkConverter converter) {
@@ -15,12 +15,12 @@
*/
package com.opensymphony.xwork2.ognl;
import com.opensymphony.xwork2.util.ProxyUtil;
import ognl.DefaultMemberAccess;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.lang.reflect.*;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -41,6 +41,7 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
private Set<Class<?>> excludedClasses = Collections.emptySet();
private Set<Pattern> excludedPackageNamePatterns = Collections.emptySet();
private Set<String> excludedPackageNames = Collections.emptySet();
private boolean disallowProxyMemberAccess;
public SecurityMemberAccess(boolean method) {
super(false);
@@ -53,6 +54,8 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
@Override
public boolean isAccessible(Map context, Object target, Member member, String propertyName) {
LOG.debug("Checking access for [target: {}, member: {}, property: {}]", target, member, propertyName);
if (checkEnumAccess(target, member)) {
LOG.trace("Allowing access to enum: {}", target);
return true;
@@ -83,6 +86,11 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
return false;
}
if (disallowProxyMemberAccess && ProxyUtil.isProxyMember(member, target)) {
LOG.warn("Access to proxy [{}] is blocked!", member);
return false;
}
boolean allow = true;
if (!checkStaticMethodAccess(member)) {
LOG.warn("Access to static [{}] is blocked!", member);
@@ -205,4 +213,8 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
public void setExcludedPackageNames(Set<String> excludedPackageNames) {
this.excludedPackageNames = excludedPackageNames;
}
public void setDisallowProxyMemberAccess(boolean disallowProxyMemberAccess) {
this.disallowProxyMemberAccess = disallowProxyMemberAccess;
}
}
@@ -57,6 +57,7 @@ public class SpringObjectFactory extends ObjectFactory implements ApplicationCon
* This is temporary solution, after validating can be removed
* @since 2.3.18
*/
@Deprecated
private boolean enableAopSupport = false;
@Inject(value="applicationContextPath",required=false)
@@ -0,0 +1,576 @@
package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.struts2.StrutsConstants;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
private static final Logger LOG = LogManager.getLogger(AbstractLocalizedTextProvider.class);
public static final String XWORK_MESSAGES_BUNDLE = "com/opensymphony/xwork2/xwork-messages";
public static final String STRUTS_MESSAGES_BUNDLE = "org/apache/struts2/struts-messages";
private static final String TOMCAT_RESOURCE_ENTRIES_FIELD = "resourceEntries";
private final String RELOADED = "com.opensymphony.xwork2.util.LocalizedTextProvider.reloaded";
protected final ConcurrentMap<String, ResourceBundle> bundlesMap = new ConcurrentHashMap<>();
protected boolean devMode = false;
protected boolean reloadBundles = false;
private final ConcurrentMap<MessageFormatKey, MessageFormat> messageFormats = new ConcurrentHashMap<>();
private final ConcurrentMap<Integer, List<String>> classLoaderMap = new ConcurrentHashMap<>();
private final Set<String> missingBundles = Collections.synchronizedSet(new HashSet<String>());
private final ConcurrentMap<Integer, ClassLoader> delegatedClassLoaderMap = new ConcurrentHashMap<>();
/**
* Add's the bundle to the internal list of default bundles.
* If the bundle already exists in the list it will be re-added.
*
* @param resourceBundleName the name of the bundle to add.
*/
@Override
public void addDefaultResourceBundle(String resourceBundleName) {
//make sure this doesn't get added more than once
final ClassLoader ccl = getCurrentThreadContextClassLoader();
synchronized (XWORK_MESSAGES_BUNDLE) {
List<String> bundles = classLoaderMap.get(ccl.hashCode());
if (bundles == null) {
bundles = new CopyOnWriteArrayList<>();
classLoaderMap.put(ccl.hashCode(), bundles);
}
bundles.remove(resourceBundleName);
bundles.add(0, resourceBundleName);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Added default resource bundle '{}' to default resource bundles for the following classloader '{}'", resourceBundleName, ccl.toString());
}
}
protected List<String> getCurrentBundleNames() {
return classLoaderMap.get(getCurrentThreadContextClassLoader().hashCode());
}
protected ClassLoader getCurrentThreadContextClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
@Inject(value = StrutsConstants.STRUTS_CUSTOM_I18N_RESOURCES, required = false)
public void setCustomI18NResources(String bundles) {
if (bundles != null && bundles.length() > 0) {
StringTokenizer customBundles = new StringTokenizer(bundles, ", ");
while (customBundles.hasMoreTokens()) {
String name = customBundles.nextToken();
try {
LOG.trace("Loading global messages from [{}]", name);
addDefaultResourceBundle(name);
} catch (Exception e) {
LOG.error(new ParameterizedMessage("Could not find messages file {}.properties. Skipping", name), e);
}
}
}
}
/**
* Returns a localized message for the specified key, aTextName. Neither the key nor the
* message is evaluated.
*
* @param aTextName the message key
* @param locale the locale the message should be for
* @return a localized message based on the specified key, or null if no localized message can be found for it
*/
@Override
public String findDefaultText(String aTextName, Locale locale) {
List<String> localList = getCurrentBundleNames();
for (String bundleName : localList) {
ResourceBundle bundle = findResourceBundle(bundleName, locale);
if (bundle != null) {
reloadBundles();
try {
return bundle.getString(aTextName);
} catch (MissingResourceException e) {
// will be logged when not found in any bundle
}
}
}
if (devMode) {
LOG.warn("Missing key [{}] in bundles [{}]!", aTextName, localList);
} else {
LOG.debug("Missing key [{}] in bundles [{}]!", aTextName, localList);
}
return null;
}
/**
* Returns a localized message for the specified key, aTextName, substituting variables from the
* array of params into the message. Neither the key nor the message is evaluated.
*
* @param aTextName the message key
* @param locale the locale the message should be for
* @param params an array of objects to be substituted into the message text
* @return A formatted message based on the specified key, or null if no localized message can be found for it
*/
@Override
public String findDefaultText(String aTextName, Locale locale, Object[] params) {
String defaultText = findDefaultText(aTextName, locale);
if (defaultText != null) {
MessageFormat mf = buildMessageFormat(defaultText, locale);
return formatWithNullDetection(mf, params);
}
return null;
}
/**
* <p>
* Finds a localized text message for the given key, aTextName, in the specified resource
* bundle.
* </p>
*
* <p>
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
* will be treated as an OGNL expression and evaluated as such.
* </p>
*
* <p>
* If a message is <b>not</b> found a WARN log will be logged.
* </p>
*
* @param bundle the bundle
* @param aTextName the key
* @param locale the locale
* @param defaultMessage the default message to use if no message was found in the bundle
* @param args arguments for the message formatter.
* @param valueStack the OGNL value stack.
* @return the localized text, or null if none can be found and no defaultMessage is provided
*/
@Override
public String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args,
ValueStack valueStack) {
try {
reloadBundles(valueStack.getContext());
String message = TextParseUtil.translateVariables(bundle.getString(aTextName), valueStack);
MessageFormat mf = buildMessageFormat(message, locale);
return formatWithNullDetection(mf, args);
} catch (MissingResourceException ex) {
if (devMode) {
LOG.warn("Missing key [{}] in bundle [{}]!", aTextName, bundle);
} else {
LOG.debug("Missing key [{}] in bundle [{}]!", aTextName, bundle);
}
}
GetDefaultMessageReturnArg result = getDefaultMessage(aTextName, locale, valueStack, args, defaultMessage);
if (unableToFindTextForKey(result)) {
LOG.warn("Unable to find text for key '{}' in ResourceBundles for locale '{}'", aTextName, locale);
}
return result != null ? result.message : null;
}
/**
* @param classLoader a {@link ClassLoader} to look up the bundle from if none can be found on the current thread's classloader
*/
public void setDelegatedClassLoader(final ClassLoader classLoader) {
synchronized (bundlesMap) {
delegatedClassLoaderMap.put(getCurrentThreadContextClassLoader().hashCode(), classLoader);
}
}
/**
* @param bundleName Removes the bundle from any cached "misses"
*/
public void clearBundle(final String bundleName) {
bundlesMap.remove(getCurrentThreadContextClassLoader().hashCode() + bundleName);
}
protected void reloadBundles() {
reloadBundles(ActionContext.getContext() != null ? ActionContext.getContext().getContextMap() : null);
}
protected void reloadBundles(Map<String, Object> context) {
if (reloadBundles) {
try {
Boolean reloaded;
if (context != null) {
reloaded = (Boolean) ObjectUtils.defaultIfNull(context.get(RELOADED), Boolean.FALSE);
} else {
reloaded = Boolean.FALSE;
}
if (!reloaded) {
bundlesMap.clear();
try {
clearMap(ResourceBundle.class, null, "cacheList");
} catch (NoSuchFieldException e) {
// happens in IBM JVM, that has a different ResourceBundle impl
// it has a 'cache' member
clearMap(ResourceBundle.class, null, "cache");
}
// now, for the true and utter hack, if we're running in tomcat, clear
// it's class loader resource cache as well.
clearTomcatCache();
if (context != null) {
context.put(RELOADED, true);
}
LOG.debug("Resource bundles reloaded");
}
} catch (Exception e) {
LOG.error("Could not reload resource bundles", e);
}
}
}
private void clearTomcatCache() {
ClassLoader loader = getCurrentThreadContextClassLoader();
// no need for compilation here.
Class cl = loader.getClass();
try {
if ("org.apache.catalina.loader.WebappClassLoader".equals(cl.getName())) {
clearMap(cl, loader, TOMCAT_RESOURCE_ENTRIES_FIELD);
} else {
LOG.debug("Class loader {} is not tomcat loader.", cl.getName());
}
} catch (NoSuchFieldException nsfe) {
if ("org.apache.catalina.loader.WebappClassLoaderBase".equals(cl.getSuperclass().getName())) {
LOG.debug("Base class {} doesn't contain '{}' field, trying with parent!", cl.getName(), TOMCAT_RESOURCE_ENTRIES_FIELD, nsfe);
try {
clearMap(cl.getSuperclass(), loader, TOMCAT_RESOURCE_ENTRIES_FIELD);
} catch (Exception e) {
LOG.warn("Couldn't clear tomcat cache using {}", cl.getSuperclass().getName(), e);
}
}
} catch (Exception e) {
LOG.warn("Couldn't clear tomcat cache", cl.getName(), e);
}
}
private void clearMap(Class cl, Object obj, String name)
throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Field field = cl.getDeclaredField(name);
field.setAccessible(true);
Object cache = field.get(obj);
synchronized (cache) {
Class ccl = cache.getClass();
Method clearMethod = ccl.getMethod("clear");
clearMethod.invoke(cache);
}
}
protected MessageFormat buildMessageFormat(String pattern, Locale locale) {
MessageFormatKey key = new MessageFormatKey(pattern, locale);
MessageFormat format = messageFormats.get(key);
if (format == null) {
format = new MessageFormat(pattern);
format.setLocale(locale);
format.applyPattern(pattern);
messageFormats.put(key, format);
}
return format;
}
protected String formatWithNullDetection(MessageFormat mf, Object[] args) {
String message = mf.format(args);
if ("null".equals(message)) {
return null;
} else {
return message;
}
}
@Inject(value = StrutsConstants.STRUTS_I18N_RELOAD, required = false)
public void setReloadBundles(String reloadBundles) {
this.reloadBundles = Boolean.parseBoolean(reloadBundles);
}
@Inject(value = StrutsConstants.STRUTS_DEVMODE, required = false)
public void setDevMode(String devMode) {
this.devMode = Boolean.parseBoolean(devMode);
}
/**
* Finds the given resource bundle by it's name.
* <p>
* Will use <code>Thread.currentThread().getContextClassLoader()</code> as the classloader.
* </p>
*
* @param aBundleName the name of the bundle (usually it's FQN classname).
* @param locale the locale.
* @return the bundle, <tt>null</tt> if not found.
*/
@Override
public ResourceBundle findResourceBundle(String aBundleName, Locale locale) {
ClassLoader classLoader = getCurrentThreadContextClassLoader();
String key = createMissesKey(String.valueOf(classLoader.hashCode()), aBundleName, locale);
if (missingBundles.contains(key)) {
return null;
}
ResourceBundle bundle = null;
try {
if (bundlesMap.containsKey(key)) {
bundle = bundlesMap.get(key);
} else {
bundle = ResourceBundle.getBundle(aBundleName, locale, classLoader);
bundlesMap.putIfAbsent(key, bundle);
}
} catch (MissingResourceException ex) {
if (delegatedClassLoaderMap.containsKey(classLoader.hashCode())) {
try {
if (bundlesMap.containsKey(key)) {
bundle = bundlesMap.get(key);
} else {
bundle = ResourceBundle.getBundle(aBundleName, locale, delegatedClassLoaderMap.get(classLoader.hashCode()));
bundlesMap.putIfAbsent(key, bundle);
}
} catch (MissingResourceException e) {
LOG.debug("Missing resource bundle [{}]!", aBundleName, e);
missingBundles.add(key);
}
} else {
LOG.debug("Missing resource bundle [{}]!", aBundleName);
missingBundles.add(key);
}
}
return bundle;
}
/**
* Clears all the internal lists.
*
* @deprecated used only in tests
*/
@Deprecated
public void reset() {
// no-op
}
/**
* Determines if we found the text in the bundles.
*
* @param result the result so far
* @return <tt>true</tt> if we could <b>not</b> find the text, <tt>false</tt> if the text was found (=success).
*/
protected boolean unableToFindTextForKey(GetDefaultMessageReturnArg result) {
if (result == null || result.message == null) {
return true;
}
// did we find it in the bundle, then no problem?
if (result.foundInBundle) {
return false;
}
// not found in bundle
return true;
}
/**
* Creates a key to used for lookup/storing in the bundle misses cache.
*
* @param prefix the prefix for the returning String - it is supposed to be the ClassLoader hash code.
* @param aBundleName the name of the bundle (usually it's FQN classname).
* @param locale the locale.
* @return the key to use for lookup/storing in the bundle misses cache.
*/
private String createMissesKey(String prefix, String aBundleName, Locale locale) {
return prefix + aBundleName + "_" + locale.toString();
}
/**
* @return the default message.
*/
protected GetDefaultMessageReturnArg getDefaultMessage(String key, Locale locale, ValueStack valueStack, Object[] args,
String defaultMessage) {
GetDefaultMessageReturnArg result = null;
boolean found = true;
if (key != null) {
String message = findDefaultText(key, locale);
if (message == null) {
message = defaultMessage;
found = false; // not found in bundles
}
// defaultMessage may be null
if (message != null) {
MessageFormat mf = buildMessageFormat(TextParseUtil.translateVariables(message, valueStack), locale);
String msg = formatWithNullDetection(mf, args);
result = new GetDefaultMessageReturnArg(msg, found);
}
}
return result;
}
/**
* @return the message from the named resource bundle.
*/
protected String getMessage(String bundleName, Locale locale, String key, ValueStack valueStack, Object[] args) {
ResourceBundle bundle = findResourceBundle(bundleName, locale);
if (bundle == null) {
return null;
}
if (valueStack != null)
reloadBundles(valueStack.getContext());
try {
String message = bundle.getString(key);
if (valueStack != null)
message = TextParseUtil.translateVariables(bundle.getString(key), valueStack);
MessageFormat mf = buildMessageFormat(message, locale);
return formatWithNullDetection(mf, args);
} catch (MissingResourceException e) {
if (devMode) {
LOG.warn("Missing key [{}] in bundle [{}]!", key, bundleName);
} else {
LOG.debug("Missing key [{}] in bundle [{}]!", key, bundleName);
}
return null;
}
}
/**
* Traverse up class hierarchy looking for message. Looks at class, then implemented interface,
* before going up hierarchy.
*
* @return the message
*/
protected String findMessage(Class clazz, String key, String indexedKey, Locale locale, Object[] args, Set<String> checked,
ValueStack valueStack) {
if (checked == null) {
checked = new TreeSet<>();
} else if (checked.contains(clazz.getName())) {
return null;
}
// look in properties of this class
String msg = getMessage(clazz.getName(), locale, key, valueStack, args);
if (msg != null) {
return msg;
}
if (indexedKey != null) {
msg = getMessage(clazz.getName(), locale, indexedKey, valueStack, args);
if (msg != null) {
return msg;
}
}
// look in properties of implemented interfaces
Class[] interfaces = clazz.getInterfaces();
for (Class anInterface : interfaces) {
msg = getMessage(anInterface.getName(), locale, key, valueStack, args);
if (msg != null) {
return msg;
}
if (indexedKey != null) {
msg = getMessage(anInterface.getName(), locale, indexedKey, valueStack, args);
if (msg != null) {
return msg;
}
}
}
// traverse up hierarchy
if (clazz.isInterface()) {
interfaces = clazz.getInterfaces();
for (Class anInterface : interfaces) {
msg = findMessage(anInterface, key, indexedKey, locale, args, checked, valueStack);
if (msg != null) {
return msg;
}
}
} else {
if (!clazz.equals(Object.class) && !clazz.isPrimitive()) {
return findMessage(clazz.getSuperclass(), key, indexedKey, locale, args, checked, valueStack);
}
}
return null;
}
static class MessageFormatKey {
String pattern;
Locale locale;
MessageFormatKey(String pattern, Locale locale) {
this.pattern = pattern;
this.locale = locale;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MessageFormatKey that = (MessageFormatKey) o;
if (pattern != null ? !pattern.equals(that.pattern) : that.pattern != null) return false;
return locale != null ? locale.equals(that.locale) : that.locale == null;
}
@Override
public int hashCode() {
int result = pattern != null ? pattern.hashCode() : 0;
result = 31 * result + (locale != null ? locale.hashCode() : 0);
return result;
}
}
static class GetDefaultMessageReturnArg {
String message;
boolean foundInBundle;
public GetDefaultMessageReturnArg(String message, boolean foundInBundle) {
this.message = message;
this.foundInBundle = foundInBundle;
}
}
}
@@ -15,15 +15,10 @@
*/
package com.opensymphony.xwork2.util;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -48,8 +43,8 @@ public class AnnotationUtils {
* Adds all fields with the specified Annotation of class clazz and its superclasses to allFields
*
* @param annotationClass the {@link Annotation}s to find
* @param clazz The {@link Class} to inspect
* @param allFields list of all fields
* @param clazz The {@link Class} to inspect
* @param allFields list of all fields
*/
public static void addAllFields(Class<? extends Annotation> annotationClass, Class clazz, List<Field> allFields) {
@@ -61,7 +56,7 @@ public class AnnotationUtils {
for (Field field : fields) {
Annotation ann = field.getAnnotation(annotationClass);
if (ann!=null) {
if (ann != null) {
allFields.add(field);
}
}
@@ -72,8 +67,8 @@ public class AnnotationUtils {
* Adds all methods with the specified Annotation of class clazz and its superclasses to allFields
*
* @param annotationClass the {@link Annotation}s to find
* @param clazz The {@link Class} to inspect
* @param allMethods list of all methods
* @param clazz The {@link Class} to inspect
* @param allMethods list of all methods
*/
public static void addAllMethods(Class<? extends Annotation> annotationClass, Class clazz, List<Method> allMethods) {
@@ -85,7 +80,7 @@ public class AnnotationUtils {
for (Method method : methods) {
Annotation ann = method.getAnnotation(annotationClass);
if (ann!=null) {
if (ann != null) {
allMethods.add(method);
}
}
@@ -93,8 +88,7 @@ public class AnnotationUtils {
}
/**
*
* @param clazz The {@link Class} to inspect
* @param clazz The {@link Class} to inspect
* @param allInterfaces list of all interfaces
*/
public static void addAllInterfaces(Class clazz, List<Class> allInterfaces) {
@@ -107,50 +101,6 @@ public class AnnotationUtils {
addAllInterfaces(clazz.getSuperclass(), allInterfaces);
}
/**
* For the given <code>Class</code> get a collection of the the {@link AnnotatedElement}s
* that match the given <code>annotation</code>s or if no <code>annotation</code>s are
* specified then return all of the annotated elements of the given <code>Class</code>.
* Includes only the method level annotations.
*
* @param clazz The {@link Class} to inspect
* @param annotation the {@link Annotation}s to find
* @return A {@link Collection}&lt;{@link AnnotatedElement}&gt; containing all of the
* method {@link AnnotatedElement}s matching the specified {@link Annotation}s
*/
public static Collection<Method> getAnnotatedMethods(Class clazz, Class<? extends Annotation>... annotation){
Collection<Method> toReturn = new HashSet<>();
for (Method m : clazz.getMethods()) {
if (ArrayUtils.isNotEmpty(annotation) && isAnnotatedBy(m, annotation)) {
toReturn.add(m);
} else if (ArrayUtils.isEmpty(annotation) && ArrayUtils.isNotEmpty(m.getAnnotations())) {
toReturn.add(m);
}
}
return toReturn;
}
/**
* Varargs version of <code>AnnotatedElement.isAnnotationPresent()</code>
* @param annotatedElement element to check
* @param annotation the {@link Annotation}s to find
* @return true is element is annotated by one of the annotation
* @see AnnotatedElement
*/
public static boolean isAnnotatedBy(AnnotatedElement annotatedElement, Class<? extends Annotation>... annotation) {
if (ArrayUtils.isEmpty(annotation)) {
return false;
}
for( Class<? extends Annotation> c : annotation ){
if( annotatedElement.isAnnotationPresent(c) ) return true;
}
return false;
}
/**
* Returns the property name for a method.
* This method is independent from property fields.
@@ -179,10 +129,10 @@ public class AnnotationUtils {
* Returns the annotation on the given class or the package of the class. This searchs up the
* class hierarchy and the package hierarchy for the closest match.
*
* @param <T> class type
* @param clazz The class to search for the annotation.
* @param annotationClass The Class of the annotation.
* @return The annotation or null.
* @param <T> class type
* @param clazz The class to search for the annotation.
* @param annotationClass The Class of the annotation.
* @return The annotation or null.
*/
public static <T extends Annotation> T findAnnotation(Class<?> clazz, Class<T> annotationClass) {
T ann = clazz.getAnnotation(annotationClass);
@@ -20,12 +20,16 @@ package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.XWorkException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* This class is an utility class that will search through the classpath
@@ -88,10 +92,24 @@ public class ClassPathFinder {
} catch (URISyntaxException e) {
continue;
}
File entry = new File(entryURI) ;
Vector<String> results = checkEntries(entry.list(), entry, "");
if (results != null ) {
matches.addAll(results);
File entry = new File(entryURI);
if (entry.isFile() && entry.toString().endsWith(".jar")) {
try {
ZipInputStream zip = new ZipInputStream(new FileInputStream(entry));
for (ZipEntry zipEntry = zip.getNextEntry(); zipEntry != null; zipEntry = zip.getNextEntry()) {
boolean doesMatch = patternMatcher.match(new HashMap<String, String>(), zipEntry.getName(), compiledPattern);
if (doesMatch) {
matches.add(zipEntry.getName());
}
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
Vector<String> results = checkEntries(entry.list(), entry, "");
if (results != null) {
matches.addAll(results);
}
}
}
return matches;
@@ -0,0 +1,260 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ModelDriven;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* Provides support for localization in the framework, it can be used to read only default bundles,
* or it can search the class hierarchy to find proper bundles.
*/
public class GlobalLocalizedTextProvider extends AbstractLocalizedTextProvider {
private static final Logger LOG = LogManager.getLogger(GlobalLocalizedTextProvider.class);
public GlobalLocalizedTextProvider() {
addDefaultResourceBundle(XWORK_MESSAGES_BUNDLE);
addDefaultResourceBundle(STRUTS_MESSAGES_BUNDLE);
}
/**
* Calls {@link #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)}
* with aTextName as the default message.
*
* @param aClass class name
* @param aTextName text name
* @param locale the locale
* @return the localized text, or null if none can be found and no defaultMessage is provided
* @see #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)
*/
@Override
public String findText(Class aClass, String aTextName, Locale locale) {
return findText(aClass, aTextName, locale, aTextName, new Object[0]);
}
/**
* <p>
* Finds a localized text message for the given key, aTextName. Both the key and the message
* itself is evaluated as required. The following algorithm is used to find the requested
* message:
* </p>
*
* <ol>
* <li>Look for message in aClass' class hierarchy.
* <ol>
* <li>Look for the message in a resource bundle for aClass</li>
* <li>If not found, look for the message in a resource bundle for any implemented interface</li>
* <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
* </ol></li>
* <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
* the model's class hierarchy (repeat sub-steps listed above).</li>
* <li>If not found, look for message in child property. This is determined by evaluating
* the message key as an OGNL expression. For example, if the key is
* <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
* object. If so, repeat the entire process fromthe beginning with the object's class as
* aClass and "address.state" as the message key.</li>
* <li>If not found, look for the message in aClass' package hierarchy.</li>
* <li>If still not found, look for the message in the default resource bundles.</li>
* <li>Return defaultMessage</li>
* </ol>
*
* <p>
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
* message for that specific key cannot be found, the general form will also be looked up
* (i.e. user.phone[*]).
* </p>
*
* <p>
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
* will be treated as an OGNL expression and evaluated as such.
* </p>
*
* @param aClass the class whose name to use as the start point for the search
* @param aTextName the key to find the text message for
* @param locale the locale the message should be for
* @param defaultMessage the message to be returned if no text message can be found in any
* resource bundle
* @param args arguments
* resource bundle
* @return the localized text, or null if none can be found and no defaultMessage is provided
*/
@Override
public String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args) {
ValueStack valueStack = ActionContext.getContext().getValueStack();
return findText(aClass, aTextName, locale, defaultMessage, args, valueStack);
}
/**
* <p>
* Finds a localized text message for the given key, aTextName. Both the key and the message
* itself is evaluated as required. The following algorithm is used to find the requested
* message:
* </p>
*
* <ol>
* <li>Look for message in aClass' class hierarchy.
* <ol>
* <li>Look for the message in a resource bundle for aClass</li>
* <li>If not found, look for the message in a resource bundle for any implemented interface</li>
* <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
* </ol></li>
* <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
* the model's class hierarchy (repeat sub-steps listed above).</li>
* <li>If not found, look for message in child property. This is determined by evaluating
* the message key as an OGNL expression. For example, if the key is
* <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
* object. If so, repeat the entire process fromthe beginning with the object's class as
* aClass and "address.state" as the message key.</li>
* <li>If not found, look for the message in aClass' package hierarchy.</li>
* <li>If still not found, look for the message in the default resource bundles.</li>
* <li>Return defaultMessage</li>
* </ol>
*
* <p>
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
* message for that specific key cannot be found, the general form will also be looked up
* (i.e. user.phone[*]).
* </p>
*
* <p>
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
* will be treated as an OGNL expression and evaluated as such.
* </p>
*
* <p>
* If a message is <b>not</b> found a WARN log will be logged.
* </p>
*
* @param aClass the class whose name to use as the start point for the search
* @param aTextName the key to find the text message for
* @param locale the locale the message should be for
* @param defaultMessage the message to be returned if no text message can be found in any
* resource bundle
* @param args arguments
* @param valueStack the value stack to use to evaluate expressions instead of the
* one in the ActionContext ThreadLocal
* @return the localized text, or null if none can be found and no defaultMessage is provided
*/
@Override
public String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack) {
String indexedTextName = null;
if (aTextName == null) {
LOG.warn("Trying to find text with null key!");
aTextName = "";
}
// calculate indexedTextName (collection[*]) if applicable
if (aTextName.contains("[")) {
int i = -1;
indexedTextName = aTextName;
while ((i = indexedTextName.indexOf("[", i + 1)) != -1) {
int j = indexedTextName.indexOf("]", i);
String a = indexedTextName.substring(0, i);
String b = indexedTextName.substring(j);
indexedTextName = a + "[*" + b;
}
}
// get default
GetDefaultMessageReturnArg result;
if (indexedTextName == null) {
result = getDefaultMessage(aTextName, locale, valueStack, args, defaultMessage);
} else {
result = getDefaultMessage(aTextName, locale, valueStack, args, null);
if (result != null && result.message != null) {
return result.message;
}
result = getDefaultMessage(indexedTextName, locale, valueStack, args, defaultMessage);
}
// could we find the text, if not log a warn
if (unableToFindTextForKey(result) && LOG.isDebugEnabled()) {
String warn = "Unable to find text for key '" + aTextName + "' ";
if (indexedTextName != null) {
warn += " or indexed key '" + indexedTextName + "' ";
}
warn += "in class '" + aClass.getName() + "' and locale '" + locale + "'";
LOG.debug(warn);
}
return result != null ? result.message : null;
}
/**
* <p>
* Finds a localized text message for the given key, aTextName, in the specified resource bundle
* with aTextName as the default message.
* </p>
*
* <p>
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
* will be treated as an OGNL expression and evaluated as such.
* </p>
*
* @param bundle a resource bundle name
* @param aTextName text name
* @param locale the locale
* @return the localized text, or null if none can be found and no defaultMessage is provided
* @see #findText(ResourceBundle, String, Locale, String, Object[])
*/
@Override
public String findText(ResourceBundle bundle, String aTextName, Locale locale) {
return findText(bundle, aTextName, locale, aTextName, new Object[0]);
}
/**
* <p>
* Finds a localized text message for the given key, aTextName, in the specified resource
* bundle.
* </p>
*
* <p>
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
* will be treated as an OGNL expression and evaluated as such.
* </p>
*
* <p>
* If a message is <b>not</b> found a WARN log will be logged.
* </p>
*
* @param bundle the bundle
* @param aTextName the key
* @param locale the locale
* @param defaultMessage the default message to use if no message was found in the bundle
* @param args arguments for the message formatter.
* @return the localized text, or null if none can be found and no defaultMessage is provided
*/
@Override
public String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args) {
ValueStack valueStack = ActionContext.getContext().getValueStack();
return findText(bundle, aTextName, locale, defaultMessage, args, valueStack);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,217 @@
/*
* Copyright 2017 The Apache Software Foundation.
*
* 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.
*/
package com.opensymphony.xwork2.util;
import org.apache.commons.lang3.reflect.ConstructorUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import java.lang.reflect.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* <code>ProxyUtil</code>
* <p>
* Various utility methods dealing with proxies
* </p>
*
*/
public class ProxyUtil {
private static final String SPRING_ADVISED_CLASS_NAME = "org.springframework.aop.framework.Advised";
private static final String SPRING_SPRINGPROXY_CLASS_NAME = "org.springframework.aop.SpringProxy";
private static final String SPRING_SINGLETONTARGETSOURCE_CLASS_NAME = "org.springframework.aop.target.SingletonTargetSource";
private static final String SPRING_TARGETCLASSAWARE_CLASS_NAME = "org.springframework.aop.TargetClassAware";
private static final Map<Class<?>, Boolean> isProxyCache =
new ConcurrentHashMap<>(256);
private static final Map<Member, Boolean> isProxyMemberCache =
new ConcurrentHashMap<>(256);
/**
* Determine the ultimate target class of the given instance, traversing
* not only a top-level proxy but any number of nested proxies as well &mdash;
* as long as possible without side effects.
* @param candidate the instance to check (might be a proxy)
* @return the ultimate target class (or the plain class of the given
* object as fallback; never {@code null})
*/
public static Class<?> ultimateTargetClass(Object candidate) {
Class<?> result = null;
if (isSpringAopProxy(candidate))
result = springUltimateTargetClass(candidate);
if (result == null) {
result = candidate.getClass();
}
return result;
}
/**
* Check whether the given object is a proxy.
* @param object the object to check
*/
public static boolean isProxy(Object object) {
Class<?> clazz = object.getClass();
Boolean flag = isProxyCache.get(clazz);
if (flag != null) {
return flag;
}
boolean isProxy = isSpringAopProxy(object);
isProxyCache.put(clazz, isProxy);
return isProxy;
}
/**
* Check whether the given member is a proxy member of a proxy object.
* @param member the member to check
* @param object the object to check
*/
public static boolean isProxyMember(Member member, Object object) {
if (!isProxy(object))
return false;
Boolean flag = isProxyMemberCache.get(member);
if (flag != null) {
return flag;
}
boolean isProxyMember = isSpringProxyMember(member);
isProxyMemberCache.put(member, isProxyMember);
return isProxyMember;
}
/**
* Determine the ultimate target class of the given spring bean instance, traversing
* not only a top-level spring proxy but any number of nested spring proxies as well &mdash;
* as long as possible without side effects, that is, just for singleton targets.
* @param candidate the instance to check (might be a spring AOP proxy)
* @return the ultimate target class (or the plain class of the given
* object as fallback; never {@code null})
*/
private static Class<?> springUltimateTargetClass(Object candidate) {
Object current = candidate;
Class<?> result = null;
while (null != current && implementsInterface(current.getClass(), SPRING_TARGETCLASSAWARE_CLASS_NAME)) {
try {
result = (Class<?>) MethodUtils.invokeMethod(current, "getTargetClass");
} catch (Throwable ignored) {
}
current = getSingletonTarget(current);
}
if (result == null) {
Class<?> clazz = candidate.getClass();
result = (isCglibProxyClass(clazz) ? clazz.getSuperclass() : candidate.getClass());
}
return result;
}
/**
* Check whether the given object is a Spring proxy.
* @param object the object to check
*/
private static boolean isSpringAopProxy(Object object) {
Class<?> clazz = object.getClass();
return (implementsInterface(clazz, SPRING_SPRINGPROXY_CLASS_NAME) && (Proxy.isProxyClass(clazz)
|| isCglibProxyClass(clazz)));
}
/**
* Check whether the given member is a member of a spring proxy.
* @param member the member to check
*/
private static boolean isSpringProxyMember(Member member) {
try {
Class<?> clazz = ClassLoaderUtil.loadClass(SPRING_ADVISED_CLASS_NAME, ProxyUtil.class);
if (hasMember(clazz, member))
return true;
clazz = ClassLoaderUtil.loadClass(SPRING_TARGETCLASSAWARE_CLASS_NAME, ProxyUtil.class);
if (hasMember(clazz, member))
return true;
clazz = ClassLoaderUtil.loadClass(SPRING_SPRINGPROXY_CLASS_NAME, ProxyUtil.class);
if (hasMember(clazz, member))
return true;
} catch (ClassNotFoundException ignored) {
}
return false;
}
/**
* Obtain the singleton target object behind the given spring proxy, if any.
* @param candidate the (potential) spring proxy to check
* @return the singleton target object, or {@code null} in any other case
* (not a spring proxy, not an existing singleton target)
*/
private static Object getSingletonTarget(Object candidate) {
try {
if (implementsInterface(candidate.getClass(), SPRING_ADVISED_CLASS_NAME)) {
Object targetSource = MethodUtils.invokeMethod(candidate, "getTargetSource");
if (implementsInterface(targetSource.getClass(), SPRING_SINGLETONTARGETSOURCE_CLASS_NAME)) {
return MethodUtils.invokeMethod(targetSource, "getTarget");
}
}
} catch (Throwable ignored) {
}
return null;
}
/**
* Check whether the specified class is a CGLIB-generated class.
* @param clazz the class to check
*/
private static boolean isCglibProxyClass(Class<?> clazz) {
return (clazz != null && clazz.getName().contains("$$"));
}
/**
* Check whether the given class implements an interface with a given class name.
* @param clazz the class to check
* @param ifaceClassName the interface class name to check
*/
private static boolean implementsInterface(Class<?> clazz, String ifaceClassName) {
try {
Class<?> ifaceClass = ClassLoaderUtil.loadClass(ifaceClassName, ProxyUtil.class);
return ifaceClass.isAssignableFrom(clazz);
} catch (ClassNotFoundException e) {
return false;
}
}
/**
* Check whether the given class has a given member.
* @param clazz the class to check
* @param member the member to check
*/
private static boolean hasMember(Class<?> clazz, Member member) {
if (member instanceof Method) {
return null != MethodUtils.getMatchingMethod(clazz, member.getName(), ((Method) member).getParameterTypes());
}
if (member instanceof Field) {
return null != FieldUtils.getField(clazz, member.getName(), true);
}
if (member instanceof Constructor) {
return null != ConstructorUtils.getMatchingAccessibleConstructor(clazz, ((Constructor) member).getParameterTypes());
}
return false;
}
}
@@ -0,0 +1,422 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.util.reflection.ReflectionProviderFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.beans.PropertyDescriptor;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* Provides support for localization in the framework, it can be used to read only default bundles,
* or it can search the class hierarchy to find proper bundles.
*/
public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
private static final Logger LOG = LogManager.getLogger(StrutsLocalizedTextProvider.class);
/**
* Clears the internal list of resource bundles.
*
* @deprecated used only in tests
*/
@Deprecated
public static void clearDefaultResourceBundles() {
// no-op
}
public StrutsLocalizedTextProvider() {
addDefaultResourceBundle(XWORK_MESSAGES_BUNDLE);
addDefaultResourceBundle(STRUTS_MESSAGES_BUNDLE);
}
/**
* Builds a {@link java.util.Locale} from a String of the form en_US_foo into a Locale
* with language "en", country "US" and variant "foo". This will parse the output of
* {@link java.util.Locale#toString()}.
*
* @param localeStr The locale String to parse.
* @param defaultLocale The locale to use if localeStr is <tt>null</tt>.
* @return requested Locale
* @deprecated please use {@link org.apache.commons.lang3.LocaleUtils#toLocale(String)}
*/
@Deprecated
public static Locale localeFromString(String localeStr, Locale defaultLocale) {
if ((localeStr == null) || (localeStr.trim().length() == 0) || ("_".equals(localeStr))) {
if (defaultLocale != null) {
return defaultLocale;
}
return Locale.getDefault();
}
int index = localeStr.indexOf('_');
if (index < 0) {
return new Locale(localeStr);
}
String language = localeStr.substring(0, index);
if (index == localeStr.length()) {
return new Locale(language);
}
localeStr = localeStr.substring(index + 1);
index = localeStr.indexOf('_');
if (index < 0) {
return new Locale(language, localeStr);
}
String country = localeStr.substring(0, index);
if (index == localeStr.length()) {
return new Locale(language, country);
}
localeStr = localeStr.substring(index + 1);
return new Locale(language, country, localeStr);
}
/**
* Calls {@link #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)}
* with aTextName as the default message.
*
* @param aClass class name
* @param aTextName text name
* @param locale the locale
* @return the localized text, or null if none can be found and no defaultMessage is provided
* @see #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)
*/
@Override
public String findText(Class aClass, String aTextName, Locale locale) {
return findText(aClass, aTextName, locale, aTextName, new Object[0]);
}
/**
* <p>
* Finds a localized text message for the given key, aTextName. Both the key and the message
* itself is evaluated as required. The following algorithm is used to find the requested
* message:
* </p>
*
* <ol>
* <li>Look for message in aClass' class hierarchy.
* <ol>
* <li>Look for the message in a resource bundle for aClass</li>
* <li>If not found, look for the message in a resource bundle for any implemented interface</li>
* <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
* </ol></li>
* <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
* the model's class hierarchy (repeat sub-steps listed above).</li>
* <li>If not found, look for message in child property. This is determined by evaluating
* the message key as an OGNL expression. For example, if the key is
* <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
* object. If so, repeat the entire process fromthe beginning with the object's class as
* aClass and "address.state" as the message key.</li>
* <li>If not found, look for the message in aClass' package hierarchy.</li>
* <li>If still not found, look for the message in the default resource bundles.</li>
* <li>Return defaultMessage</li>
* </ol>
*
* <p>
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
* message for that specific key cannot be found, the general form will also be looked up
* (i.e. user.phone[*]).
* </p>
*
* <p>
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
* will be treated as an OGNL expression and evaluated as such.
* </p>
*
* @param aClass the class whose name to use as the start point for the search
* @param aTextName the key to find the text message for
* @param locale the locale the message should be for
* @param defaultMessage the message to be returned if no text message can be found in any
* resource bundle
* @param args arguments
* resource bundle
* @return the localized text, or null if none can be found and no defaultMessage is provided
*/
@Override
public String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args) {
ValueStack valueStack = ActionContext.getContext().getValueStack();
return findText(aClass, aTextName, locale, defaultMessage, args, valueStack);
}
/**
* <p>
* Finds a localized text message for the given key, aTextName. Both the key and the message
* itself is evaluated as required. The following algorithm is used to find the requested
* message:
* </p>
*
* <ol>
* <li>Look for message in aClass' class hierarchy.
* <ol>
* <li>Look for the message in a resource bundle for aClass</li>
* <li>If not found, look for the message in a resource bundle for any implemented interface</li>
* <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
* </ol></li>
* <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
* the model's class hierarchy (repeat sub-steps listed above).</li>
* <li>If not found, look for message in child property. This is determined by evaluating
* the message key as an OGNL expression. For example, if the key is
* <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
* object. If so, repeat the entire process fromthe beginning with the object's class as
* aClass and "address.state" as the message key.</li>
* <li>If not found, look for the message in aClass' package hierarchy.</li>
* <li>If still not found, look for the message in the default resource bundles.</li>
* <li>Return defaultMessage</li>
* </ol>
*
* <p>
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
* message for that specific key cannot be found, the general form will also be looked up
* (i.e. user.phone[*]).
* </p>
*
* <p>
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
* will be treated as an OGNL expression and evaluated as such.
* </p>
*
* <p>
* If a message is <b>not</b> found a WARN log will be logged.
* </p>
*
* @param aClass the class whose name to use as the start point for the search
* @param aTextName the key to find the text message for
* @param locale the locale the message should be for
* @param defaultMessage the message to be returned if no text message can be found in any
* resource bundle
* @param args arguments
* @param valueStack the value stack to use to evaluate expressions instead of the
* one in the ActionContext ThreadLocal
* @return the localized text, or null if none can be found and no defaultMessage is provided
*/
@Override
public String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args,
ValueStack valueStack) {
String indexedTextName = null;
if (aTextName == null) {
LOG.warn("Trying to find text with null key!");
aTextName = "";
}
// calculate indexedTextName (collection[*]) if applicable
if (aTextName.contains("[")) {
int i = -1;
indexedTextName = aTextName;
while ((i = indexedTextName.indexOf("[", i + 1)) != -1) {
int j = indexedTextName.indexOf("]", i);
String a = indexedTextName.substring(0, i);
String b = indexedTextName.substring(j);
indexedTextName = a + "[*" + b;
}
}
// search up class hierarchy
String msg = findMessage(aClass, aTextName, indexedTextName, locale, args, null, valueStack);
if (msg != null) {
return msg;
}
if (ModelDriven.class.isAssignableFrom(aClass)) {
ActionContext context = ActionContext.getContext();
// search up model's class hierarchy
ActionInvocation actionInvocation = context.getActionInvocation();
// ActionInvocation may be null if we're being run from a Sitemesh filter, so we won't get model texts if this is null
if (actionInvocation != null) {
Object action = actionInvocation.getAction();
if (action instanceof ModelDriven) {
Object model = ((ModelDriven) action).getModel();
if (model != null) {
msg = findMessage(model.getClass(), aTextName, indexedTextName, locale, args, null, valueStack);
if (msg != null) {
return msg;
}
}
}
}
}
// nothing still? alright, search the package hierarchy now
for (Class clazz = aClass;
(clazz != null) && !clazz.equals(Object.class);
clazz = clazz.getSuperclass()) {
String basePackageName = clazz.getName();
while (basePackageName.lastIndexOf('.') != -1) {
basePackageName = basePackageName.substring(0, basePackageName.lastIndexOf('.'));
String packageName = basePackageName + ".package";
msg = getMessage(packageName, locale, aTextName, valueStack, args);
if (msg != null) {
return msg;
}
if (indexedTextName != null) {
msg = getMessage(packageName, locale, indexedTextName, valueStack, args);
if (msg != null) {
return msg;
}
}
}
}
// see if it's a child property
int idx = aTextName.indexOf(".");
if (idx != -1) {
String newKey = null;
String prop = null;
if (aTextName.startsWith(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX)) {
idx = aTextName.indexOf(".", XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length());
if (idx != -1) {
prop = aTextName.substring(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length(), idx);
newKey = XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX + aTextName.substring(idx + 1);
}
} else {
prop = aTextName.substring(0, idx);
newKey = aTextName.substring(idx + 1);
}
if (prop != null) {
Object obj = valueStack.findValue(prop);
try {
Object actionObj = ReflectionProviderFactory.getInstance().getRealTarget(prop, valueStack.getContext(), valueStack.getRoot());
if (actionObj != null) {
PropertyDescriptor propertyDescriptor = ReflectionProviderFactory.getInstance().getPropertyDescriptor(actionObj.getClass(), prop);
if (propertyDescriptor != null) {
Class clazz = propertyDescriptor.getPropertyType();
if (clazz != null) {
if (obj != null) {
valueStack.push(obj);
}
msg = findText(clazz, newKey, locale, null, args);
if (obj != null) {
valueStack.pop();
}
if (msg != null) {
return msg;
}
}
}
}
} catch (Exception e) {
LOG.debug("unable to find property {}", prop, e);
}
}
}
// get default
GetDefaultMessageReturnArg result;
if (indexedTextName == null) {
result = getDefaultMessage(aTextName, locale, valueStack, args, defaultMessage);
} else {
result = getDefaultMessage(aTextName, locale, valueStack, args, null);
if (result != null && result.message != null) {
return result.message;
}
result = getDefaultMessage(indexedTextName, locale, valueStack, args, defaultMessage);
}
// could we find the text, if not log a warn
if (unableToFindTextForKey(result) && LOG.isDebugEnabled()) {
String warn = "Unable to find text for key '" + aTextName + "' ";
if (indexedTextName != null) {
warn += " or indexed key '" + indexedTextName + "' ";
}
warn += "in class '" + aClass.getName() + "' and locale '" + locale + "'";
LOG.debug(warn);
}
return result != null ? result.message : null;
}
/**
* <p>
* Finds a localized text message for the given key, aTextName, in the specified resource bundle
* with aTextName as the default message.
* </p>
*
* <p>
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
* will be treated as an OGNL expression and evaluated as such.
* </p>
*
* @param bundle a resource bundle name
* @param aTextName text name
* @param locale the locale
* @return the localized text, or null if none can be found and no defaultMessage is provided
* @see #findText(java.util.ResourceBundle, String, java.util.Locale, String, Object[])
*/
@Override
public String findText(ResourceBundle bundle, String aTextName, Locale locale) {
return findText(bundle, aTextName, locale, aTextName, new Object[0]);
}
/**
* <p>
* Finds a localized text message for the given key, aTextName, in the specified resource
* bundle.
* </p>
*
* <p>
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
* will be treated as an OGNL expression and evaluated as such.
* </p>
*
* <p>
* If a message is <b>not</b> found a WARN log will be logged.
* </p>
*
* @param bundle the bundle
* @param aTextName the key
* @param locale the locale
* @param defaultMessage the default message to use if no message was found in the bundle
* @param args arguments for the message formatter.
* @return the localized text, or null if none can be found and no defaultMessage is provided
*/
@Override
public String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args) {
ValueStack valueStack = ActionContext.getContext().getValueStack();
return findText(bundle, aTextName, locale, defaultMessage, args, valueStack);
}
}
@@ -31,8 +31,6 @@ import java.util.*;
*/
public class TextParseUtil {
private static final int MAX_RECURSION = 1;
/**
* Converts all instances of ${...}, and %{...} in <code>expression</code> to the value returned
* by a call to {@link ValueStack#findValue(java.lang.String)}. If an item cannot
@@ -108,7 +106,7 @@ public class TextParseUtil {
* @return Converted object from variable translation.
*/
public static Object translateVariables(char open, String expression, ValueStack stack, Class asType, ParsedValueEvaluator evaluator) {
return translateVariables(new char[]{open} , expression, stack, asType, evaluator, MAX_RECURSION);
return translateVariables(new char[]{open} , expression, stack, asType, evaluator, TextParser.DEFAULT_LOOP_COUNT);
}
/**
@@ -122,7 +120,7 @@ public class TextParseUtil {
* @return Converted object from variable translation.
*/
public static Object translateVariables(char[] openChars, String expression, ValueStack stack, Class asType, ParsedValueEvaluator evaluator) {
return translateVariables(openChars, expression, stack, asType, evaluator, MAX_RECURSION);
return translateVariables(openChars, expression, stack, asType, evaluator, TextParser.DEFAULT_LOOP_COUNT);
}
/**
@@ -178,7 +176,7 @@ public class TextParseUtil {
* @return converted objects
*/
public static Collection<String> translateVariablesCollection(String expression, ValueStack stack, boolean excludeEmptyElements, ParsedValueEvaluator evaluator) {
return translateVariablesCollection(new char[]{'$', '%'}, expression, stack, excludeEmptyElements, evaluator, MAX_RECURSION);
return translateVariablesCollection(new char[]{'$', '%'}, expression, stack, excludeEmptyElements, evaluator, TextParser.DEFAULT_LOOP_COUNT);
}
/**
@@ -6,6 +6,8 @@ package com.opensymphony.xwork2.util;
*/
public interface TextParser {
int DEFAULT_LOOP_COUNT = 1;
Object evaluate(char[] openChars, String expression, TextParseUtil.ParsedValueEvaluator evaluator, int maxLoopCount);
}
@@ -29,7 +29,7 @@ import com.opensymphony.xwork2.util.location.LocatableProperties;
public class XWorkTestCaseHelper {
public static ConfigurationManager setUp() throws Exception {
ConfigurationManager configurationManager = new ConfigurationManager();
ConfigurationManager configurationManager = new ConfigurationManager(Container.DEFAULT_NAME);
configurationManager.addContainerProvider(new XWorkConfigurationProvider());
Configuration config = configurationManager.getConfiguration();
Container container = config.getContainer();
@@ -40,7 +40,7 @@ public class XWorkTestCaseHelper {
ActionContext.setContext(new ActionContext(stack.getContext()));
// clear out localization
LocalizedTextUtil.reset();
//container.getInstance(LocalizedTextUtil.class).reset();
//ObjectFactory.setObjectFactory(container.getInstance(ObjectFactory.class));
@@ -54,7 +54,7 @@ public class XWorkTestCaseHelper {
} catch (Exception e) {
throw new RuntimeException("Cannot clean old configuration", e);
}
configurationManager = new ConfigurationManager();
configurationManager = new ConfigurationManager(Container.DEFAULT_NAME);
configurationManager.addContainerProvider(new ContainerProvider() {
public void destroy() {}
public void init(Configuration configuration) throws ConfigurationException {}
@@ -2,11 +2,11 @@ package com.opensymphony.xwork2.util.fs;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.XWorkConstants;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.util.HashSet;
import java.util.Set;
@@ -19,6 +19,7 @@ public class DefaultFileManagerFactory implements FileManagerFactory {
private static final Logger LOG = LogManager.getLogger(DefaultFileManagerFactory.class);
private boolean reloadingConfigs;
private FileManagerHolder fileManagerHolder;
private FileManager systemFileManager;
private Container container;
@@ -32,20 +33,27 @@ public class DefaultFileManagerFactory implements FileManagerFactory {
this.container = container;
}
@Inject(value = XWorkConstants.RELOAD_XML_CONFIGURATION, required = false)
@Inject(value = StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, required = false)
public void setReloadingConfigs(String reloadingConfigs) {
this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
}
public FileManager getFileManager() {
if (fileManagerHolder != null) {
return fileManagerHolder.getFileManager();
}
FileManager fileManager = lookupFileManager();
if (fileManager != null) {
LOG.debug("Using FileManager implementation [{}]", fileManager.getClass().getSimpleName());
fileManager.setReloadingConfigs(reloadingConfigs);
fileManagerHolder = new FileManagerHolder(fileManager);
return fileManager;
}
LOG.debug("Using default implementation of FileManager provided under name [system]: {}", systemFileManager.getClass().getSimpleName());
systemFileManager.setReloadingConfigs(reloadingConfigs);
fileManagerHolder = new FileManagerHolder(systemFileManager);
return systemFileManager;
}
@@ -77,4 +85,17 @@ public class DefaultFileManagerFactory implements FileManagerFactory {
return null;
}
private static class FileManagerHolder {
private final FileManager fileManager;
public FileManagerHolder(FileManager fileManager) {
this.fileManager = fileManager;
}
public FileManager getFileManager() {
return fileManager;
}
}
}
@@ -33,7 +33,9 @@ import java.lang.reflect.Proxy;
/**
* @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
* @deprecated will be dropped with next major release (2.6)
*/
@Deprecated
public class ObjectProfiler {
/**
@@ -135,6 +137,10 @@ public class ObjectProfiler {
}
/**
* @deprecated will be dropped with next major release (2.6)
*/
@Deprecated
class TimerInvocationHandler implements InvocationHandler {
protected Object target;
@@ -35,7 +35,10 @@ import java.util.List;
* @author <a href="mailto:mike@atlassian.com">Mike Cannon-Brookes</a>
* @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
* @version $Date$ $Id$
*
* @deprecated will be dropped with next major release (2.6)
*/
@Deprecated
public class ProfilingTimerBean implements java.io.Serializable {
private static final long serialVersionUID = -6180672043920208784L;
@@ -266,7 +266,10 @@ import org.apache.logging.log4j.Logger;
* </p>
*
* <!-- END SNIPPET: profileLogFile -->
*
* @deprecated will be dropped with next major release (2.6)
*/
@Deprecated
public class UtilTimerStack {
// A reference to the current ProfilingTimerBean
@@ -72,7 +72,23 @@ public interface ReflectionProvider {
* note if exclusions AND inclusions are supplied and not null nothing will get copied.
*/
void copy(Object from, Object to, Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions);
/**
* Copies the properties in the object "from" and sets them in the object "to"
* only setting properties defined in the given "editable" class (or interface)
* using specified type converter, or {@link com.opensymphony.xwork2.conversion.impl.XWorkConverter} if none
* is specified.
*
* @param from the source object
* @param to the target object
* @param context the action context we're running under
* @param exclusions collection of method names to excluded from copying ( can be null)
* @param inclusions collection of method names to included copying (can be null)
* note if exclusions AND inclusions are supplied and not null nothing will get copied.
* @param editable the class (or interface) to restrict property setting to
*/
void copy(Object from, Object to, Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions, Class<?> editable);
/**
* Looks for the real target with the specified property given a root Object which may be a
* CompoundRoot.
@@ -52,6 +52,7 @@ public class AnnotationActionValidatorManager implements ActionValidatorManager
private ValidatorFileParser validatorFileParser;
private FileManager fileManager;
private boolean reloadingConfigs;
private TextProviderFactory textProviderFactory;
@Inject
public void setValidatorFactory(ValidatorFactory fac) {
@@ -73,6 +74,11 @@ public class AnnotationActionValidatorManager implements ActionValidatorManager
this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
}
@Inject
public void setTextProviderFactory(TextProviderFactory textProviderFactory) {
this.textProviderFactory = textProviderFactory;
}
public List<Validator> getValidators(Class clazz, String context) {
return getValidators(clazz, context, null);
}
@@ -116,7 +122,7 @@ public class AnnotationActionValidatorManager implements ActionValidatorManager
}
public void validate(Object object, String context, String method) throws ValidationException {
ValidatorContext validatorContext = new DelegatingValidatorContext(object);
ValidatorContext validatorContext = new DelegatingValidatorContext(object, textProviderFactory);
validate(object, context, validatorContext, method);
}
@@ -15,6 +15,7 @@
*/
package com.opensymphony.xwork2.validator;
import com.opensymphony.xwork2.util.AnnotationUtils;
import com.opensymphony.xwork2.validator.annotations.*;
import org.apache.commons.lang3.StringUtils;
@@ -24,8 +25,6 @@ import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <code>AnnotationValidationConfigurationBuilder</code>
@@ -36,9 +35,6 @@ import java.util.regex.Pattern;
*/
public class AnnotationValidationConfigurationBuilder {
private static final Pattern SETTER_PATTERN = Pattern.compile("set([A-Z][A-Za-z0-9]*)$");
private static final Pattern GETTER_PATTERN = Pattern.compile("(get|is|has)([A-Z][A-Za-z0-9]*)$");
private ValidatorFactory validatorFactory;
public AnnotationValidationConfigurationBuilder(ValidatorFactory fac) {
@@ -61,7 +57,7 @@ public class AnnotationValidationConfigurationBuilder {
if (o instanceof Method) {
Method method = (Method) o;
fieldName = resolvePropertyName(method);
fieldName = AnnotationUtils.resolvePropertyName(method);
methodName = method.getName();
annotations = method.getAnnotations();
@@ -126,6 +122,14 @@ public class AnnotationValidationConfigurationBuilder {
result.add(temp);
}
}
// Process CrediCardValidator
else if (a instanceof CreditCardValidator) {
CreditCardValidator v = (CreditCardValidator) a;
ValidatorConfig temp = processCreditCardValidatorAnnotation(v, fieldName, methodName);
if (temp != null) {
result.add(temp);
}
}
// Process FieldExpressionValidator
else if (a instanceof FieldExpressionValidator) {
FieldExpressionValidator v = (FieldExpressionValidator) a;
@@ -142,6 +146,14 @@ public class AnnotationValidationConfigurationBuilder {
result.add(temp);
}
}
// Process LongRangeFieldValidator
else if (a instanceof LongRangeFieldValidator) {
LongRangeFieldValidator v = (LongRangeFieldValidator) a;
ValidatorConfig temp = processLongRangeFieldValidatorAnnotation(v, fieldName, methodName);
if (temp != null) {
result.add(temp);
}
}
// Process ShortRangeFieldValidator
else if (a instanceof ShortRangeFieldValidator) {
ShortRangeFieldValidator v = (ShortRangeFieldValidator) a;
@@ -267,6 +279,15 @@ public class AnnotationValidationConfigurationBuilder {
}
}
}
CreditCardValidator[] ccv = validations.creditCards();
if (ccv != null) {
for (CreditCardValidator v : ccv) {
ValidatorConfig temp = processCreditCardValidatorAnnotation(v, fieldName, methodName);
if (temp != null) {
result.add(temp);
}
}
}
FieldExpressionValidator[] fev = validations.fieldExpressions();
if (fev != null) {
for (FieldExpressionValidator v : fev) {
@@ -285,6 +306,15 @@ public class AnnotationValidationConfigurationBuilder {
}
}
}
LongRangeFieldValidator[] lrfv = validations.longRangeFields();
if (irfv != null) {
for (LongRangeFieldValidator v : lrfv) {
ValidatorConfig temp = processLongRangeFieldValidatorAnnotation(v, fieldName, methodName);
if (temp != null) {
result.add(temp);
}
}
}
RegexFieldValidator[] rfv = validations.regexFields();
if (rfv != null) {
for (RegexFieldValidator v : rfv) {
@@ -659,6 +689,41 @@ public class AnnotationValidationConfigurationBuilder {
.build();
}
private ValidatorConfig processLongRangeFieldValidatorAnnotation(LongRangeFieldValidator v, String fieldName, String methodName) {
String validatorType = "long";
Map<String, Object> params = new HashMap<>();
if (fieldName != null) {
params.put("fieldName", fieldName);
} else if (StringUtils.isNotEmpty(v.fieldName())) {
params.put("fieldName", v.fieldName());
}
if (v.min() != null && v.min().length() > 0) {
params.put("min", v.min());
}
if (v.max() != null && v.max().length() > 0) {
params.put("max", v.max());
}
if (StringUtils.isNotEmpty(v.maxExpression())) {
params.put("maxExpression", v.maxExpression());
}
if (StringUtils.isNotEmpty(v.minExpression())) {
params.put("minExpression", v.minExpression());
}
validatorFactory.lookupRegisteredValidatorType(validatorType);
return new ValidatorConfig.Builder(validatorType)
.addParams(params)
.addParam("methodName", methodName)
.shortCircuit(v.shortCircuit())
.defaultMessage(v.message())
.messageKey(v.key())
.messageParams(v.messageParams())
.build();
}
private ValidatorConfig processShortRangeFieldValidatorAnnotation(ShortRangeFieldValidator v, String fieldName, String methodName) {
String validatorType = "short";
@@ -790,6 +855,28 @@ public class AnnotationValidationConfigurationBuilder {
.build();
}
private ValidatorConfig processCreditCardValidatorAnnotation(CreditCardValidator v, String fieldName, String methodName) {
String validatorType = "creditcard";
Map<String, Object> params = new HashMap<>();
if (fieldName != null) {
params.put("fieldName", fieldName);
} else if (StringUtils.isNotEmpty(v.fieldName())) {
params.put("fieldName", v.fieldName());
}
validatorFactory.lookupRegisteredValidatorType(validatorType);
return new ValidatorConfig.Builder(validatorType)
.addParams(params)
.addParam("methodName", methodName)
.shortCircuit(v.shortCircuit())
.defaultMessage(v.message())
.messageKey(v.key())
.messageParams(v.messageParams())
.build();
}
private ValidatorConfig processDateRangeFieldValidatorAnnotation(DateRangeFieldValidator v, String fieldName, String methodName) {
String validatorType = "date";
@@ -874,28 +961,4 @@ public class AnnotationValidationConfigurationBuilder {
}
/**
* Returns the property name for a method.
* This method is independant from property fields.
*
* @param method The method to get the property name for.
* @return the property name for given method; null if non could be resolved.
*/
public String resolvePropertyName(Method method) {
Matcher matcher = SETTER_PATTERN.matcher(method.getName());
if (matcher.matches() && method.getParameterTypes().length == 1) {
String raw = matcher.group(1);
return raw.substring(0, 1).toLowerCase() + raw.substring(1);
}
matcher = GETTER_PATTERN.matcher(method.getName());
if (matcher.matches() && method.getParameterTypes().length == 0) {
String raw = matcher.group(2);
return raw.substring(0, 1).toLowerCase() + raw.substring(1);
}
return null;
}
}
@@ -18,6 +18,7 @@ package com.opensymphony.xwork2.validator;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.XWorkConstants;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
@@ -50,16 +51,19 @@ import java.util.*;
*/
public class DefaultActionValidatorManager implements ActionValidatorManager {
private final static Logger LOG = LogManager.getLogger(DefaultActionValidatorManager.class);
/** The file suffix for any validation file. */
protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
private final Map<String, List<ValidatorConfig>> validatorCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
private final Map<String, List<ValidatorConfig>> validatorFileCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
private final Logger LOG = LogManager.getLogger(DefaultActionValidatorManager.class);
private ValidatorFactory validatorFactory;
private ValidatorFileParser validatorFileParser;
private FileManager fileManager;
private boolean reloadingConfigs;
private TextProviderFactory textProviderFactory;
@Inject
public void setValidatorFileParser(ValidatorFileParser parser) {
@@ -81,6 +85,11 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
}
@Inject
public void setTextProviderFactory(TextProviderFactory textProviderFactory) {
this.textProviderFactory = textProviderFactory;
}
public synchronized List<Validator> getValidators(Class clazz, String context) {
return getValidators(clazz, context, null);
}
@@ -118,7 +127,7 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
}
public void validate(Object object, String context, String method) throws ValidationException {
ValidatorContext validatorContext = new DelegatingValidatorContext(object);
ValidatorContext validatorContext = new DelegatingValidatorContext(object, textProviderFactory);
validate(object, context, validatorContext, method);
}
@@ -57,10 +57,10 @@ public class DelegatingValidatorContext implements ValidatorContext {
*
* @param object the object to use for validation (usually an Action).
*/
public DelegatingValidatorContext(Object object) {
public DelegatingValidatorContext(Object object, TextProviderFactory textProviderFactory) {
this.localeProvider = makeLocaleProvider(object);
this.validationAware = makeValidationAware(object);
this.textProvider = makeTextProvider(object, localeProvider);
this.textProvider = makeTextProvider(object, textProviderFactory);
}
/**
@@ -68,10 +68,13 @@ public class DelegatingValidatorContext implements ValidatorContext {
* the validation context are created based on the class.
*
* @param clazz the class to initialize the context with.
*
* @deprecated will be removed, do not use!
*/
@Deprecated
public DelegatingValidatorContext(Class clazz) {
localeProvider = new ActionContextLocaleProvider();
textProvider = new TextProviderFactory().createInstance(clazz, localeProvider);
textProvider = new StrutsTextProviderFactory().createInstance(clazz);
validationAware = new LoggingValidationAware(clazz);
}
@@ -193,18 +196,11 @@ public class DelegatingValidatorContext implements ValidatorContext {
return validationAware.hasFieldErrors();
}
public static TextProvider makeTextProvider(Object object, LocaleProvider localeProvider) {
public TextProvider makeTextProvider(Object object, TextProviderFactory textProviderFactory) {
// the object argument passed through here will most probably be an ActionSupport descendant which does
// implements TextProvider.
if (object != null && object instanceof DelegatingValidatorContext) {
return ((DelegatingValidatorContext) object).getTextProvider();
} else if (object != null && localeProvider != null && localeProvider instanceof DelegatingValidatorContext) {
return new CompositeTextProvider(new TextProvider[]{
new TextProviderFactory().createInstance(object.getClass(), localeProvider),
((DelegatingValidatorContext)localeProvider).getTextProvider()
});
} else if (localeProvider != null && localeProvider instanceof DelegatingValidatorContext) {
return ((DelegatingValidatorContext)localeProvider).getTextProvider();
}
if ((object != null) && (object instanceof TextProvider)) {
@@ -213,20 +209,11 @@ public class DelegatingValidatorContext implements ValidatorContext {
}
return new CompositeTextProvider(new TextProvider[]{
((TextProvider) object),
new TextProviderSupport(object.getClass(), localeProvider)
});
} else if (localeProvider != null && localeProvider instanceof TextProvider) {
if (localeProvider instanceof CompositeTextProvider) {
return (CompositeTextProvider) localeProvider;
}
return new CompositeTextProvider(new TextProvider[]{
((TextProvider) localeProvider),
new TextProviderSupport(localeProvider.getClass(), localeProvider)
textProviderFactory.createInstance(object.getClass())
});
} else {
return new TextProviderFactory().createInstance(
object != null ? object.getClass() : DelegatingValidatorContext.class,
localeProvider);
return textProviderFactory.createInstance(
object != null ? object.getClass() : DelegatingValidatorContext.class);
}
}
@@ -266,18 +253,30 @@ public class DelegatingValidatorContext implements ValidatorContext {
* An implementation of LocaleProvider which gets the locale from the action context.
*/
private static class ActionContextLocaleProvider implements LocaleProvider {
private LocaleProvider localeProvider;
private LocaleProvider getLocaleProvider() {
if (localeProvider == null) {
LocaleProviderFactory localeProviderFactory = ActionContext.getContext().getInstance(LocaleProviderFactory.class);
localeProvider = localeProviderFactory.createLocaleProvider();
}
return localeProvider;
}
@Override
public Locale getLocale() {
return ActionContext.getContext().getInstance(LocaleProvider.class).getLocale();
return getLocaleProvider().getLocale();
}
@Override
public boolean isValidLocaleString(String localeStr) {
return ActionContext.getContext().getInstance(LocaleProvider.class).isValidLocaleString(localeStr);
return getLocaleProvider().isValidLocaleString(localeStr);
}
@Override
public boolean isValidLocale(Locale locale) {
return ActionContext.getContext().getInstance(LocaleProvider.class).isValidLocale(locale);
return getLocaleProvider().isValidLocale(locale);
}
}
@@ -129,15 +129,15 @@ import org.apache.logging.log4j.Logger;
*/
public class ValidationInterceptor extends MethodFilterInterceptor {
private static final Logger LOG = LogManager.getLogger(ValidationInterceptor.class);
private final static String VALIDATE_PREFIX = "validate";
private final static String ALT_VALIDATE_PREFIX = "validateDo";
private boolean validateAnnotatedMethodOnly;
private ActionValidatorManager actionValidatorManager;
private static final Logger LOG = LogManager.getLogger(ValidationInterceptor.class);
private final static String VALIDATE_PREFIX = "validate";
private final static String ALT_VALIDATE_PREFIX = "validateDo";
private boolean alwaysInvokeValidate = true;
private boolean programmatic = true;
private boolean declarative = true;
@@ -212,8 +212,8 @@ public class ValidationInterceptor extends MethodFilterInterceptor {
String context = this.getValidationContext(proxy);
String method = proxy.getMethod();
if (log.isDebugEnabled()) {
log.debug("Validating {}/{} with method {}.", invocation.getProxy().getNamespace(), invocation.getProxy().getActionName(), method);
if (LOG.isDebugEnabled()) {
LOG.debug("Validating {}/{} with method {}.", invocation.getProxy().getNamespace(), invocation.getProxy().getActionName(), method);
}
@@ -0,0 +1,66 @@
/*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* 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.
*/
package com.opensymphony.xwork2.validator.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This validator checks that a field is a valid credit card.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CreditCardValidator {
/**
* @return The default error message for this validator.
* NOTE: It is required to set a message, if you are not using the message key for 18n lookup!
*/
String message() default "";
/**
* @return The message key to lookup for i18n.
*/
String key() default "";
/**
* @return Additional params to be used to customize message - will be evaluated against the Value Stack
*/
String[] messageParams() default {};
/**
* @return The optional fieldName for SIMPLE validator types.
*/
String fieldName() default "";
/**
* If this is activated, the validator will be used as short-circuit.
*
* Adds the short-circuit='true' attribute value if <tt>true</tt>.
*
* @return true if validator will be used as short-circuit. Default is false.
*/
boolean shortCircuit() default false;
/**
* @return The validation type for this field/method.
*/
ValidatorType type() default ValidatorType.FIELD;
}
@@ -0,0 +1,194 @@
/*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* 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.
*/
package com.opensymphony.xwork2.validator.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: description -->
* This validator checks that a numeric field has a value within a specified range.
* If neither min nor max is set, nothing will be done.
* <!-- END SNIPPET: description -->
*
* <p><u>Annotation usage:</u></p>
*
* <!-- START SNIPPET: usage -->
* <p>The annotation must be applied at method level.</p>
* <!-- END SNIPPET: usage -->
*
* <p><u>Annotation parameters:</u></p>
*
* <!-- START SNIPPET: parameters -->
* <table class='confluenceTable' summary=''>
* <tr>
* <th class='confluenceTh'> Parameter </th>
* <th class='confluenceTh'> Required </th>
* <th class='confluenceTh'> Default </th>
* <th class='confluenceTh'> Notes </th>
* </tr>
* <tr>
* <td class='confluenceTd'>message</td>
* <td class='confluenceTd'>yes</td>
* <td class='confluenceTd'>&nbsp;</td>
* <td class='confluenceTd'>field error message</td>
* </tr>
* <tr>
* <td class='confluenceTd'>key</td>
* <td class='confluenceTd'>no</td>
* <td class='confluenceTd'>&nbsp;</td>
* <td class='confluenceTd'>i18n key from language specific properties file.</td>
* </tr>
* <tr>
* <td class='confluenceTd'>messageParams</td>
* <td class='confluenceTd'>no</td>
* <td class='confluenceTd'>&nbsp;</td>
* <td class='confluenceTd'>Additional params to be used to customize message - will be evaluated against the Value Stack</td>
* </tr>
* <tr>
* <td class='confluenceTd'>fieldName</td>
* <td class='confluenceTd'>no</td>
* <td class='confluenceTd'>&nbsp;</td>
* <td class='confluenceTd'>&nbsp;</td>
* </tr>
* <tr>
* <td class='confluenceTd'>shortCircuit</td>
* <td class='confluenceTd'>no</td>
* <td class='confluenceTd'>false</td>
* <td class='confluenceTd'>If this validator should be used as shortCircuit.</td>
* </tr>
* <tr>
* <td class='confluenceTd'>type</td>
* <td class='confluenceTd'>yes</td>
* <td class='confluenceTd'>ValidatorType.FIELD</td>
* <td class='confluenceTd'>Enum value from ValidatorType. Either FIELD or SIMPLE can be used here.</td>
* </tr>
* <tr>
* <td class='confluenceTd'> min </td>
* <td class='confluenceTd'> no </td>
* <td class='confluenceTd'>&nbsp;</td>
* <td class='confluenceTd'> Integer property. The minimum the number must be.</td>
* </tr>
* <tr>
* <td class='confluenceTd'> minExpression </td>
* <td class='confluenceTd'> no </td>
* <td class='confluenceTd'>&nbsp;</td>
* <td class='confluenceTd'>OGNL expression used to obtain the minimum the number must be.</td>
* </tr>
* <tr>
* <td class='confluenceTd'> max </td>
* <td class='confluenceTd'> no </td>
* <td class='confluenceTd'>&nbsp;</td>
* <td class='confluenceTd'> Integer property. The maximum number can be. Can be an expression.!</td>
* </tr>
* <tr>
* <td class='confluenceTd'> maxExpression </td>
* <td class='confluenceTd'> no </td>
* <td class='confluenceTd'>&nbsp;</td>
* <td class='confluenceTd'>OGNL expression used to obtain the maximum number can be.</td>
* </tr>
* <tr>
* <td class='confluenceTd'>parse</td>
* <td class='confluenceTd'>no</td>
* <td class='confluenceTd'>false</td>
* <td class='confluenceTd'>Enable parsing of min/max value.</td>
* </tr>
* </table>
*
* <p>If neither <em>min</em> nor <em>max</em> is set, nothing will be done.</p>
*
* <p>The values for min and max must be inserted as String values so that "0" can be handled as a possible value.</p>
* <!-- END SNIPPET: parameters -->
*
* <p><u>Example code:</u></p>
*
* <pre>
* <!-- START SNIPPET: example -->
* &#64;LongRangeFieldValidator(message = "Default message", key = "i18n.key", shortCircuit = true, min = "0", max = "42")
*
* &#64;LongRangeFieldValidator(message = "Default message", key = "i18n.key", shortCircuit = true, minExpression = "${minValue}", maxExpression = "${maxValue}")
* <!-- END SNIPPET: example -->
* </pre>
*
*
* @author Rainer Hermanns
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LongRangeFieldValidator {
/**
* Long property.
*
* @return The minimum the number must be.
*/
String min() default "";
/**
* @return The minimum number can be defined as an expression
*/
String minExpression() default "";
/**
* Long property.
*
* @return The maximum number can be.
*/
String max() default "";
/**
* @return The maximum number can be defined as an expression
*/
String maxExpression() default "";
/**
* @return The default error message for this validator.
* NOTE: It is required to set a message, if you are not using the message key for 18n lookup!
*/
String message() default "";
/**
* @return The message key to lookup for i18n.
*/
String key() default "";
/**
* @return Additional params to be used to customize message - will be evaluated against the Value Stack
*/
String[] messageParams() default {};
/**
* @return The optional fieldName for SIMPLE validator types.
*/
String fieldName() default "";
/**
* If this is activated, the validator will be used as short-circuit.
*
* Adds the short-circuit='true' attribute value if <tt>true</tt>.
*
* @return true if validator will be used as short-circuit. Default is false.
*/
boolean shortCircuit() default false;
/**
* @return The validation type for this field/method.
*/
ValidatorType type() default ValidatorType.FIELD;
}
@@ -129,6 +129,8 @@ import java.lang.annotation.Target;
* {&#64;StringLengthFieldValidator(type = ValidatorType.SIMPLE, trim = true, minLength="10" , maxLength = "12", fieldName = "needstringlength", message = "You must enter a stringlength.")},
* intRangeFields =
* { &#64;IntRangeFieldValidator(type = ValidatorType.SIMPLE, fieldName = "intfield", min = "6", max = "10", message = "bar must be between ${min} and ${max}, current value is ${bar}.")},
* longRangeFields =
* { &#64;LongRangeFieldValidator(type = ValidatorType.SIMPLE, fieldName = "intfield", min = "6", max = "10", message = "bar must be between ${min} and ${max}, current value is ${bar}.")},
* shortRangeFields =
* { &#64;ShortRangeFieldValidator(type = ValidatorType.SIMPLE, fieldName = "shortfield", min = "1", max = "128", message = "bar must be between ${min} and ${max}, current value is ${bar}.")},
* dateRangeFields =
@@ -167,10 +169,14 @@ public @interface Validations {
EmailValidator[] emails() default {};
CreditCardValidator[] creditCards() default {};
FieldExpressionValidator[] fieldExpressions() default {};
IntRangeFieldValidator[] intRangeFields() default {};
LongRangeFieldValidator[] longRangeFields() default {};
RequiredFieldValidator[] requiredFields() default {};
RequiredStringValidator[] requiredStrings() default {};
@@ -1,6 +1,8 @@
package com.opensymphony.xwork2.validator.validators;
import com.opensymphony.xwork2.validator.ValidationException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* <!-- START SNIPPET: javadoc -->
@@ -29,6 +31,8 @@ import com.opensymphony.xwork2.validator.ValidationException;
*/
public class ConditionalVisitorFieldValidator extends VisitorFieldValidator {
private static final Logger LOG = LogManager.getLogger(ConditionalVisitorFieldValidator.class);
private String expression;
public void setExpression(String expression) {
@@ -75,7 +79,7 @@ public class ConditionalVisitorFieldValidator extends VisitorFieldValidator {
if ((obj != null) && (obj instanceof Boolean)) {
answer = (Boolean) obj;
} else {
log.warn("Got result of {} when trying to get Boolean.", obj);
LOG.warn("Got result of {} when trying to get Boolean.", obj);
}
return answer;
@@ -0,0 +1,46 @@
/*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* 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.
*/
package com.opensymphony.xwork2.validator.validators;
import org.apache.commons.lang3.StringUtils;
/**
* CreditCardFieldValidator checks that a given String/Array/Collection field,
* if not empty, is a valid credit card number.
*/
public class CreditCardValidator extends RegexFieldValidator {
public static final String CREDIT_CARD_PATTERN =
"^(?:4[0-9]{12}(?:[0-9]{3})?" + // Visa
"|(?:5[1-5][0-9]{2}" + // MasterCard
"|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}" +
"|3[47][0-9]{13}" + // American Express
"|3(?:0[0-5]|[68][0-9])[0-9]{11}" + // Diners Club
"|6(?:011|5[0-9]{2})[0-9]{12}" + // Discover
"|(?:2131|1800|35\\d{3})\\d{11}" + // JCB
")$";
public CreditCardValidator() {
setRegex(CREDIT_CARD_PATTERN);
setCaseSensitive(false);
}
protected void validateFieldValue(Object object, String value, String regexToUse) {
super.validateFieldValue(object, StringUtils.deleteWhitespace(value), regexToUse);
}
}
@@ -18,6 +18,11 @@ package com.opensymphony.xwork2.validator.validators;
import com.opensymphony.xwork2.validator.ValidationException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
import java.util.Collection;
/**
* <!-- START SNIPPET: javadoc -->
@@ -86,7 +91,9 @@ import org.apache.commons.lang3.StringUtils;
* @author Rene Gielen
*/
public class DoubleRangeFieldValidator extends FieldValidatorSupport {
private static final Logger LOG = LogManager.getLogger(DoubleRangeFieldValidator.class);
private Double maxInclusive = null;
private Double minInclusive = null;
private Double minExclusive = null;
@@ -99,14 +106,8 @@ public class DoubleRangeFieldValidator extends FieldValidatorSupport {
public void validate(Object object) throws ValidationException {
String fieldName = getFieldName();
Double value;
try {
Object obj = this.getFieldValue(fieldName, object);
if (obj == null) {
return;
}
value = Double.valueOf(obj.toString());
} catch (NumberFormatException e) {
Object obj = this.getFieldValue(fieldName, object);
if (obj == null) {
return;
}
@@ -115,11 +116,37 @@ public class DoubleRangeFieldValidator extends FieldValidatorSupport {
Double maxExclusiveToUse = getMaxExclusive();
Double minExclusiveToUse = getMinExclusive();
if ((maxInclusiveToUse != null && value.compareTo(maxInclusiveToUse) > 0) ||
(minInclusiveToUse != null && value.compareTo(minInclusiveToUse) < 0) ||
(maxExclusiveToUse != null && value.compareTo(maxExclusiveToUse) >= 0) ||
(minExclusiveToUse != null && value.compareTo(minExclusiveToUse) <= 0)) {
addFieldError(fieldName, object);
if (obj.getClass().isArray()) {
Object[] values = (Object[]) obj;
validateCollection(maxInclusiveToUse, minInclusiveToUse, maxExclusiveToUse, minExclusiveToUse, Arrays.asList(values));
} else if (Collection.class.isAssignableFrom(obj.getClass())) {
Collection values = (Collection) obj;
validateCollection(maxInclusiveToUse, minInclusiveToUse, maxExclusiveToUse, minExclusiveToUse, values);
} else {
validateValue(obj, maxInclusiveToUse, minInclusiveToUse, maxExclusiveToUse, minExclusiveToUse);
}
}
protected void validateCollection(Double maxInclusiveToUse, Double minInclusiveToUse, Double maxExclusiveToUse, Double minExclusiveToUse, Collection values) {
for (Object objValue : values) {
validateValue(objValue, maxInclusiveToUse, minInclusiveToUse, maxExclusiveToUse, minExclusiveToUse);
}
}
protected void validateValue(Object obj, Double maxInclusiveToUse, Double minInclusiveToUse, Double maxExclusiveToUse, Double minExclusiveToUse) {
try {
setCurrentValue(obj);
Double value = Double.valueOf(obj.toString());
if ((maxInclusiveToUse != null && value.compareTo(maxInclusiveToUse) > 0) ||
(minInclusiveToUse != null && value.compareTo(minInclusiveToUse) < 0) ||
(maxExclusiveToUse != null && value.compareTo(maxExclusiveToUse) >= 0) ||
(minExclusiveToUse != null && value.compareTo(minExclusiveToUse) <= 0)) {
addFieldError(getFieldName(), value);
}
} catch (NumberFormatException e) {
LOG.debug("Cannot validate value {} - not a Double", e);
} finally {
setCurrentValue(null);
}
}
@@ -23,7 +23,7 @@ package com.opensymphony.xwork2.validator.validators;
* The regular expression used to validate that the string is an email address is:
*
* <pre>
* \\b^['_a-z0-9-\\+]+(\\.['_a-z0-9-\\+]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2}|aero|arpa|asia|biz|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|nato|net|org|pro|tel|travel|xxx|tech|cat)$\\b
* \\b^['_a-z0-9-\\+]+(\\.['_a-z0-9-\\+]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2,6})$\\b
* </pre>
*
* You can also specify expression, caseSensitive and trim params as a OGNL expression, see the example below.
@@ -78,9 +78,7 @@ package com.opensymphony.xwork2.validator.validators;
*/
public class EmailValidator extends RegexFieldValidator {
// see XW-371
public static final String EMAIL_ADDRESS_PATTERN =
"\\b^['_a-z0-9-\\+]+(\\.['_a-z0-9-\\+]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2}|aero|arpa|asia|biz|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|nato|net|org|pro|tel|travel|xxx|tech|cat)$\\b";
public static final String EMAIL_ADDRESS_PATTERN = "\\b^['_a-z0-9-\\+]+(\\.['_a-z0-9-\\+]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2,6})$\\b";
public EmailValidator() {
setRegex(EMAIL_ADDRESS_PATTERN);
@@ -16,6 +16,8 @@
package com.opensymphony.xwork2.validator.validators;
import com.opensymphony.xwork2.validator.ValidationException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* <!-- START SNIPPET: javadoc -->
@@ -44,6 +46,8 @@ import com.opensymphony.xwork2.validator.ValidationException;
*/
public class ExpressionValidator extends ValidatorSupport {
private static final Logger LOG = LogManager.getLogger(ExpressionValidator.class);
private String expression;
public void setExpression(String expression) {
@@ -69,11 +73,11 @@ public class ExpressionValidator extends ValidatorSupport {
if ((obj != null) && (obj instanceof Boolean)) {
answer = (Boolean) obj;
} else {
log.warn("Got result of [{}] when trying to get Boolean.", obj);
LOG.warn("Got result of [{}] when trying to get Boolean.", obj);
}
if (!answer) {
log.debug("Validation failed on expression [{}] with validated object [{}]", expression, object);
LOG.debug("Validation failed on expression [{}] with validated object [{}]", expression, object);
addActionError(object);
}
}
@@ -16,6 +16,8 @@
package com.opensymphony.xwork2.validator.validators;
import com.opensymphony.xwork2.validator.ValidationException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
@@ -55,6 +57,8 @@ import com.opensymphony.xwork2.validator.ValidationException;
*/
public class FieldExpressionValidator extends FieldValidatorSupport {
private static final Logger LOG = LogManager.getLogger(FieldExpressionValidator.class);
private String expression;
public void setExpression(String expression) {
@@ -82,10 +86,10 @@ public class FieldExpressionValidator extends FieldValidatorSupport {
if ((obj != null) && (obj instanceof Boolean)) {
answer = (Boolean) obj;
} else {
log.warn("Got result of {} when trying to get Boolean.", obj);
LOG.warn("Got result of {} when trying to get Boolean.", obj);
}
if (!answer.booleanValue()) {
if (!answer) {
addFieldError(fieldName, object);
}
}
@@ -17,16 +17,17 @@ package com.opensymphony.xwork2.validator.validators;
import com.opensymphony.xwork2.validator.FieldValidator;
/**
* Base class for field validators.
*
* @author Jason Carreira
* You can access fieldName and its currentValue in a message using expression, e.g.
* "Wrong value ${currentValue} for ${fieldName}"
*/
public abstract class FieldValidatorSupport extends ValidatorSupport implements FieldValidator {
private String fieldName;
private String type;
protected String fieldName;
protected String type;
protected Object currentValue;
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
@@ -45,4 +46,12 @@ public abstract class FieldValidatorSupport extends ValidatorSupport implements
public String getValidatorType() {
return type;
}
public Object getCurrentValue() {
return currentValue;
}
void setCurrentValue(Object currentValue) {
this.currentValue = currentValue;
}
}
@@ -20,6 +20,8 @@ import org.apache.logging.log4j.LogManager;
import com.opensymphony.xwork2.validator.ValidationException;
import org.apache.commons.lang3.StringUtils;
import java.util.Collection;
/**
* Base class for range based validators. Use this class to develop any other custom range validators.
*/
@@ -40,25 +42,45 @@ public abstract class RangeValidatorSupport<T extends Comparable> extends FieldV
public void validate(Object object) throws ValidationException {
Object obj = getFieldValue(getFieldName(), object);
Comparable<T> value = (Comparable<T>) obj;
// if there is no value - don't do comparison
// if a value is required, a required validator should be added to the field
if (value == null) {
if (obj == null) {
return;
}
T min = getMin();
T max = getMax();
if (obj.getClass().isArray()) {
Object[] values = (Object[]) obj;
for (Object objValue : values) {
validateValue(object, (Comparable<T>) objValue, min, max);
}
} else if (Collection.class.isAssignableFrom(obj.getClass())) {
Collection<?> values = (Collection<?>) obj;
for (Object objValue : values) {
validateValue(object, (Comparable<T>) objValue, min, max);
}
} else {
validateValue(object, (Comparable<T>) obj, min, max);
}
}
protected void validateValue(Object object, Comparable<T> value, T min, T max) {
setCurrentValue(value);
// only check for a minimum value if the min parameter is set
T minComparatorValue = getMin();
if ((minComparatorValue != null) && (value.compareTo(minComparatorValue) < 0)) {
if ((min != null) && (value.compareTo(min) < 0)) {
addFieldError(getFieldName(), object);
}
// only check for a maximum value if the max parameter is set
T maxComparatorValue = getMax();
if ((maxComparatorValue != null) && (value.compareTo(maxComparatorValue) > 0)) {
if ((max != null) && (value.compareTo(max) > 0)) {
addFieldError(getFieldName(), object);
}
setCurrentValue(null);
}
public void setMin(T min) {
@@ -66,13 +88,11 @@ public abstract class RangeValidatorSupport<T extends Comparable> extends FieldV
}
public T getMin() {
if (min != null) {
return min;
} else if (StringUtils.isNotEmpty(minExpression)) {
return (T) parse(minExpression, type);
} else {
return null;
}
return getT(min, minExpression, type);
}
public T getMax() {
return getT(max, maxExpression, type);
}
public void setMinExpression(String minExpression) {
@@ -84,19 +104,19 @@ public abstract class RangeValidatorSupport<T extends Comparable> extends FieldV
this.max = max;
}
public T getMax() {
if (max != null) {
return max;
} else if (StringUtils.isNotEmpty(maxExpression)) {
return (T) parse(maxExpression, type);
} else {
return null;
}
}
public void setMaxExpression(String maxExpression) {
LOG.debug("${maxExpression} was defined as [{}]", maxExpression);
this.maxExpression = maxExpression;
}
protected T getT(T minMax, String minMaxExpression, Class<T> toType) {
if (minMax != null) {
return minMax;
} else if (StringUtils.isNotEmpty(minMaxExpression)) {
return (T) parse(minMaxExpression, toType);
} else {
return null;
}
}
}
@@ -16,11 +16,17 @@
package com.opensymphony.xwork2.validator.validators;
import com.opensymphony.xwork2.ObjectFactory;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import com.opensymphony.xwork2.validator.ValidationException;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -89,9 +95,9 @@ public class RegexFieldValidator extends FieldValidatorSupport {
private String regex;
private String regexExpression;
private Boolean caseSensitive = true;
private String caseSensitiveExpression = "";
private String caseSensitiveExpression = EMPTY_STRING;
private Boolean trim = true;
private String trimExpression = "";
private String trimExpression = EMPTY_STRING;
public void validate(Object object) throws ValidationException {
String fieldName = getFieldName();
@@ -102,17 +108,30 @@ public class RegexFieldValidator extends FieldValidatorSupport {
LOG.debug("Defined regexp as [{}]", regexToUse);
if (value == null || regexToUse == null) {
LOG.debug("Either value is empty (please use a required validator) or regex is empty");
return;
}
// XW-375 - must be a string
if (!(value instanceof String)) {
return;
if (value.getClass().isArray()) {
Object[] values = (Object[]) value;
for (Object objValue: values) {
validateFieldValue(object, Objects.toString(objValue, EMPTY_STRING), regexToUse);
}
} else if (Collection.class.isAssignableFrom(value.getClass())) {
Collection values = (Collection) value;
for (Object objValue : values) {
validateFieldValue(object, Objects.toString(objValue, EMPTY_STRING), regexToUse);
}
} else {
validateFieldValue(object, Objects.toString(value, EMPTY_STRING), regexToUse);
}
}
protected void validateFieldValue(Object object, String value, String regexToUse) {
// string must not be empty
String str = ((String) value).trim();
String str = value.trim();
if (str.length() == 0) {
LOG.debug("Value is empty, please use a required validator");
return;
}
@@ -124,14 +143,19 @@ public class RegexFieldValidator extends FieldValidatorSupport {
pattern = Pattern.compile(regexToUse, Pattern.CASE_INSENSITIVE);
}
String compare = (String) value;
if ( isTrimed() ) {
String compare = value;
if (isTrimed()) {
compare = compare.trim();
}
Matcher matcher = pattern.matcher( compare );
if (!matcher.matches()) {
addFieldError(fieldName, object);
try {
setCurrentValue(compare);
Matcher matcher = pattern.matcher(compare);
if (!matcher.matches()) {
addFieldError(fieldName, object);
}
} finally {
setCurrentValue(null);
}
}
@@ -17,6 +17,9 @@ package com.opensymphony.xwork2.validator.validators;
import com.opensymphony.xwork2.validator.ValidationException;
import java.lang.reflect.Array;
import java.util.Collection;
/**
* <!-- START SNIPPET: javadoc -->
@@ -63,6 +66,10 @@ public class RequiredFieldValidator extends FieldValidatorSupport {
if (value == null) {
addFieldError(fieldName, object);
} else if (value.getClass().isArray() && Array.getLength(value) == 0) {
addFieldError(fieldName, object);
} else if (Collection.class.isAssignableFrom(value.getClass()) && ((Collection) value).size() == 0) {
addFieldError(fieldName, object);
}
}
}
@@ -17,6 +17,7 @@ package com.opensymphony.xwork2.validator.validators;
import com.opensymphony.xwork2.validator.ValidationException;
import java.util.Collection;
/**
* <!-- START SNIPPET: javadoc -->
@@ -85,21 +86,51 @@ public class RequiredStringValidator extends FieldValidatorSupport {
}
public void validate(Object object) throws ValidationException {
String fieldName = getFieldName();
Object value = this.getFieldValue(fieldName, object);
Object fieldValue = this.getFieldValue(getFieldName(), object);
if (!(value instanceof String)) {
addFieldError(fieldName, object);
if (fieldValue == null) {
addFieldError(getFieldName(), object);
return;
}
if (fieldValue.getClass().isArray()) {
Object[] values = (Object[]) fieldValue;
for (Object value : values) {
validateValue(object, value);
}
} else if (Collection.class.isAssignableFrom(fieldValue.getClass())) {
Collection values = (Collection) fieldValue;
for (Object value : values) {
validateValue(object, value);
}
} else {
String s = (String) value;
validateValue(object, fieldValue);
}
}
if (trim) {
s = s.trim();
protected void validateValue(Object object, Object fieldValue) {
try {
setCurrentValue(fieldValue);
if (fieldValue == null) {
addFieldError(getFieldName(), object);
return;
}
if (s.length() == 0) {
addFieldError(fieldName, object);
if (fieldValue instanceof String) {
String stingValue = (String) fieldValue;
if (trim) {
stingValue = stingValue.trim();
}
if (stingValue.length() == 0) {
addFieldError(getFieldName(), object);
}
} else {
addFieldError(getFieldName(), object);
}
} finally {
setCurrentValue(null);
}
}
@@ -17,6 +17,11 @@ package com.opensymphony.xwork2.validator.validators;
import com.opensymphony.xwork2.validator.ValidationException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collection;
import java.util.Objects;
/**
* <!-- START SNIPPET: javadoc -->
@@ -84,6 +89,8 @@ import org.apache.commons.lang3.StringUtils;
*/
public class StringLengthFieldValidator extends FieldValidatorSupport {
private static final Logger LOG = LogManager.getLogger(StringLengthFieldValidator.class);
private boolean trim = true;
private int maxLength = -1;
private int minLength = -1;
@@ -138,17 +145,37 @@ public class StringLengthFieldValidator extends FieldValidatorSupport {
}
public void validate(Object object) throws ValidationException {
String fieldName = getFieldName();
String val = (String) getFieldValue(fieldName, object);
Object fieldValue = getFieldValue(fieldName, object);
if (StringUtils.isEmpty(val)) {
// use a required validator for these
if (fieldValue == null) {
LOG.debug("Value for field {} is null, use a required validator", getFieldName());
} else if (fieldValue.getClass().isArray()) {
Object[] values = (Object[]) fieldValue;
for (Object value : values) {
validateValue(object, value);
}
} else if (Collection.class.isAssignableFrom(fieldValue.getClass())) {
Collection values = (Collection) fieldValue;
for (Object value : values) {
validateValue(object, value);
}
} else {
validateValue(object, fieldValue);
}
}
protected void validateValue(Object object, Object value) {
String stringValue = Objects.toString(value, "");
if (StringUtils.isEmpty(stringValue)) {
LOG.debug("Value is empty, use a required validator");
return;
}
if (isTrim()) {
val = val.trim();
if (val.length() <= 0) {
// use a required validator
stringValue = stringValue.trim();
if (StringUtils.isEmpty(stringValue)) {
LOG.debug("Value is empty, use a required validator");
return;
}
}
@@ -156,10 +183,15 @@ public class StringLengthFieldValidator extends FieldValidatorSupport {
int minLengthToUse = getMinLength();
int maxLengthToUse = getMaxLength();
if ((minLengthToUse > -1) && (val.length() < minLengthToUse)) {
addFieldError(fieldName, object);
} else if ((maxLengthToUse > -1) && (val.length() > maxLengthToUse)) {
addFieldError(fieldName, object);
try {
setCurrentValue(stringValue);
if ((minLengthToUse > -1) && (stringValue.length() < minLengthToUse)) {
addFieldError(fieldName, object);
} else if ((maxLengthToUse > -1) && (stringValue.length() > maxLengthToUse)) {
addFieldError(fieldName, object);
}
} finally {
setCurrentValue(null);
}
}
@@ -20,6 +20,8 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collection;
import java.util.Objects;
import java.util.regex.Pattern;
/**
@@ -65,19 +67,46 @@ public class URLValidator extends FieldValidatorSupport {
private Pattern urlPattern = Pattern.compile(DEFAULT_URL_REGEX, Pattern.CASE_INSENSITIVE);
public void validate(Object object) throws ValidationException {
String fieldName = getFieldName();
Object value = this.getFieldValue(fieldName, object);
Object value = getFieldValue(fieldName, object);
// if there is no value - don't do comparison
// if a value is required, a required validator should be added to the field
if (value == null || value.toString().length() == 0) {
String stringValue = Objects.toString(value, EMPTY_STRING).trim();
if (stringValue.length() == 0) {
LOG.debug("Value for field {} is empty, won't ba validated, please use a required validator", fieldName);
return;
}
String stringValue = String.valueOf(value).trim();
if (value.getClass().isArray()) {
Object[] values = (Object[]) value;
for (Object objValue : values) {
LOG.debug("Validating element of array: {}", objValue);
validateValue(object, objValue);
}
} else if (Collection.class.isAssignableFrom(value.getClass())) {
Collection values = (Collection) value;
for (Object objValue : values) {
LOG.debug("Validating element of collection: {}", objValue);
validateValue(object, objValue);
}
} else {
LOG.debug("Validating field: {}", value);
validateValue(object, value);
}
}
if (!(value.getClass().equals(String.class)) || !getUrlPattern().matcher(stringValue).matches()) {
addFieldError(fieldName, object);
protected void validateValue(Object object, Object value) {
String stringValue = Objects.toString(value, EMPTY_STRING).trim();
if (stringValue.length() == 0) {
LOG.debug("Value for field {} is empty, won't ba validated, please use a required validator", fieldName);
return;
}
try {
setCurrentValue(value);
if (!(value.getClass().equals(String.class)) || !getUrlPattern().matcher(stringValue).matches()) {
addFieldError(fieldName, object);
}
} finally {
setCurrentValue(null);
}
}
@@ -15,6 +15,8 @@
*/
package com.opensymphony.xwork2.validator.validators;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.validator.*;
@@ -35,16 +37,24 @@ import java.util.List;
*/
public abstract class ValidatorSupport implements Validator, ShortCircuitableValidator {
protected final Logger log = LogManager.getLogger(this.getClass());
private static final Logger LOG = LogManager.getLogger(ValidatorSupport.class);
public static final String EMPTY_STRING = "";
protected String defaultMessage = "";
protected String messageKey;
private ValidatorContext validatorContext;
private boolean shortCircuit;
private String type;
private String[] messageParameters;
protected ValueStack stack;
protected String defaultMessage = "";
protected String messageKey;
protected ValueStack stack;
protected TextProviderFactory textProviderFactory;
@Inject
public void setTextProviderFactory(TextProviderFactory textProviderFactory) {
this.textProviderFactory = textProviderFactory;
}
public void setValueStack(ValueStack stack) {
this.stack = stack;
@@ -76,7 +86,7 @@ public abstract class ValidatorSupport implements Validator, ShortCircuitableVal
defaultMessage = messageKey;
}
if (validatorContext == null) {
validatorContext = new DelegatingValidatorContext(object);
validatorContext = new DelegatingValidatorContext(object, textProviderFactory);
}
List<Object> parsedMessageParameters = null;
if (messageParameters != null) {
@@ -89,7 +99,7 @@ public abstract class ValidatorSupport implements Validator, ShortCircuitableVal
} catch (Exception e) {
// if there's an exception in parsing, we'll just treat the expression itself as the
// parameter
log.warn("exception while parsing message parameter [{}]", messageParameter, e);
LOG.warn("exception while parsing message parameter [{}]", messageParameter, e);
parsedMessageParameters.add(messageParameter);
}
}
@@ -16,14 +16,20 @@
package com.opensymphony.xwork2.validator.validators;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.CompositeTextProvider;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.validator.ActionValidatorManager;
import com.opensymphony.xwork2.validator.DelegatingValidatorContext;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.ValidatorContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
@@ -81,6 +87,8 @@ import java.util.Collection;
*/
public class VisitorFieldValidator extends FieldValidatorSupport {
private static final Logger LOG = LogManager.getLogger(VisitorFieldValidator.class);
private String context;
private boolean appendPrefix = true;
private ActionValidatorManager actionValidatorManager;
@@ -121,7 +129,7 @@ public class VisitorFieldValidator extends FieldValidatorSupport {
String fieldName = getFieldName();
Object value = this.getFieldValue(fieldName, object);
if (value == null) {
log.warn("The visited object is null, VisitorValidator will not be able to handle validation properly. Please make sure the visited object is not null for VisitorValidator to function properly");
LOG.warn("The visited object is null, VisitorValidator will not be able to handle validation properly. Please make sure the visited object is not null for VisitorValidator to function properly");
return;
}
ValueStack stack = ActionContext.getContext().getValueStack();
@@ -166,24 +174,37 @@ public class VisitorFieldValidator extends FieldValidatorSupport {
ValidatorContext validatorContext;
if (appendPrefix) {
validatorContext = new AppendingValidatorContext(getValidatorContext(), o, fieldName, getMessage(o));
ValidatorContext parent = getValidatorContext();
validatorContext = new AppendingValidatorContext(parent, createTextProvider(o, parent), fieldName, getMessage(o));
} else {
ValidatorContext parent = getValidatorContext();
validatorContext = new DelegatingValidatorContext(parent, DelegatingValidatorContext.makeTextProvider(o, parent), parent);
CompositeTextProvider textProvider = createTextProvider(o, parent);
validatorContext = new DelegatingValidatorContext(parent, textProvider, parent);
}
actionValidatorManager.validate(o, visitorContext, validatorContext);
stack.pop();
}
private CompositeTextProvider createTextProvider(Object o, ValidatorContext parent) {
List<TextProvider> textProviders = new LinkedList<>();
if (o instanceof TextProvider) {
textProviders.add((TextProvider) o);
} else {
textProviders.add(textProviderFactory.createInstance(o.getClass()));
}
textProviders.add(parent);
return new CompositeTextProvider(textProviders);
}
public static class AppendingValidatorContext extends DelegatingValidatorContext {
private String field;
private String message;
private ValidatorContext parent;
public AppendingValidatorContext(ValidatorContext parent, Object object, String field, String message) {
super(parent, makeTextProvider(object, parent), parent);
public AppendingValidatorContext(ValidatorContext parent, TextProvider textProvider, String field, String message) {
super(parent, textProvider, parent);
this.field = field;
this.message = message;
@@ -139,6 +139,13 @@ public final class StrutsConstants {
*/
public static final String STRUTS_MULTIPART_PARSER = "struts.multipart.parser";
/**
* A global switch to disable support for multipart requests
*/
public static final String STRUTS_MULTIPART_ENABLED = "struts.multipart.enabled";
public static final String STRUTS_MULTIPART_VALIDATION_REGEX = "struts.multipart.validationRegex";
/** How Spring should autowire. Valid values are 'name', 'type', 'auto', and 'constructor' */
public static final String STRUTS_OBJECTFACTORY_SPRING_AUTOWIRE = "struts.objectFactory.spring.autoWire";
@@ -152,6 +159,7 @@ public final class StrutsConstants {
public static final String STRUTS_OBJECTFACTORY_SPRING_USE_CLASS_CACHE = "struts.objectFactory.spring.useClassCache";
/** Uses different logic to construct beans, see https://issues.apache.org/jira/browse/WW-4110 */
@Deprecated
public static final String STRUTS_OBJECTFACTORY_SPRING_ENABLE_AOP_SUPPORT = "struts.objectFactory.spring.enableAopSupport";
/** Whether or not XSLT templates should not be cached */
@@ -200,9 +208,16 @@ public final class StrutsConstants {
/** XWork default text provider */
public static final String STRUTS_XWORKTEXTPROVIDER = "struts.xworkTextProvider";
/** The {@link com.opensymphony.xwork2.LocaleProvider} implementation class */
/**
* The {@link com.opensymphony.xwork2.LocaleProvider} implementation class
* @deprecated use {@link StrutsConstants#STRUTS_LOCALE_PROVIDER_FACTORY} instead
*/
@Deprecated
public static final String STRUTS_LOCALE_PROVIDER = "struts.localeProvider";
/** The {@link com.opensymphony.xwork2.LocaleProviderFactory} implementation class */
public static final String STRUTS_LOCALE_PROVIDER_FACTORY = "struts.localeProviderFactory";
/** The name of the parameter to create when mapping an id (used by some action mappers) */
public static final String STRUTS_ID_PARAMETER_NAME = "struts.mapper.idParameterName";
@@ -306,4 +321,10 @@ public final class StrutsConstants {
public static final String STRUTS_CONTENT_TYPE_MATCHER = "struts.contentTypeMatcher";
public static final String STRUTS_SMI_METHOD_REGEX = "struts.strictMethodInvocation.methodRegex";
public static final String STRUTS_TEXT_PROVIDER_FACTORY = "struts.textProviderFactory";
public static final String STRUTS_LOCALIZED_TEXT_PROVIDER = "struts.localizedTextProvider";
public static final String STRUTS_DISALLOW_PROXY_MEMBER_ACCESS = "struts.disallowProxyMemberAccess";
}

Some files were not shown because too many files have changed in this diff Show More