tag:blogger.com,1999:blog-30304766495882436382024-03-12T00:14:56.002-07:00nascentเรียนรู้สู่การเป็นผู้สร้าง softwarejittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.comBlogger444125tag:blogger.com,1999:blog-3030476649588243638.post-20098538457954310452019-07-30T20:15:00.000-07:002019-07-30T20:17:16.114-07:00ตัวอย่างการเขียน Java Spring-boot Reactive (WebFlux) <div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0QKfGvzCQX45LhAwATA8QJPexcQgCG05T_dwt7wnGQolHhvC60LZsG8JJGPG9PJeBObRMuP_NH4wRl_RVXHThx0uUXfeNGcUIwQLoGW6iEvnZzqVB4WEa1gjJ5ShN0R-F8qHI9C-LfJnE/s1600/reactive_spring.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="198" data-original-width="648" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0QKfGvzCQX45LhAwATA8QJPexcQgCG05T_dwt7wnGQolHhvC60LZsG8JJGPG9PJeBObRMuP_NH4wRl_RVXHThx0uUXfeNGcUIwQLoGW6iEvnZzqVB4WEa1gjJ5ShN0R-F8qHI9C-LfJnE/s640/reactive_spring.png" style="border: none;" width="640" /></a></div>
<br />
ผมได้ทำตัวอย่างการเขียน Java Spring-boot WebFlux ซึ่งเป็นการเขียน Spring-boot แบบ Non-Blocking I/O หรือ Asynchronous หากใครสนใจสามารถเข้าไปศึกษาได้ที่<br />
<span style="background-color: white; color: #24292e; font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol"; font-size: 16px;"><br /></span>
<a href="https://github.com/jittagornp/spring-boot-webflux-example">https://github.com/jittagornp/spring-boot-webflux-example</a><br />
<span style="background-color: white; color: #24292e; font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol"; font-size: 16px;"><br /></span>jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-18325937536961233632019-06-12T19:58:00.001-07:002020-01-20T17:53:55.957-08:00ถึงคนอ่าน Blog และผู้ติดตามทุกคนน่ะครับ<div>
<div>
Blog นี้ผมไม่ได้เขียนมาซักระยะนึงแล้ว </div>
<div>
จริง ๆ ต้องเรียกว่านานมาก ๆ เลยแหล่ะ (เกือบ 4 ปีได้ ฮ่าๆๆๆ)</div>
<div>
<br /></div>
<div>
ตอนนี้ผมย้ายไปเขียนบน platform อื่น ๆ</div>
<div>
หลัง ๆ มาก็มี เขียนบ้าง ไม่ได้เขียนบ้าง </div>
<div>
เพราะมีหน้าที่รับผิดชอบที่เยอะขึ้น เลยไม่ค่อยได้มีเวลาเขียน Blog ครับ</div>
<div>
บวกกับตอนนี้มีความสนใจ เรื่องใดเรื่องนึงเป็นพิเศษ เลยไม่ได้มีเวลามาทำตรงนี้</div>
<div>
<br /></div>
<div>
ถ้าผู้อ่าน เพื่อน ๆ พี่ ๆ น้อง ๆ คนไหนยังอยากติดตามอยู่ สามารถติดตามได้ตาม link นี้ครับ</div>
<div>
<br /></div>
<div>
<br />
<span style="font-size: x-large;"><b>Jittagornp.me</b></span><br />
<a href="https://www.jittagornp.me/">https://www.jittagornp.me/</a><br />
<br />
<br /></div>
<div>
</div>
<div>
</div>
</div>
<div>
ขอบคุณครับ</div>
jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-65691970685147634302015-05-25T02:17:00.002-07:002015-05-25T02:35:51.651-07:00maven ทำ jar file ให้ double click run program ได้ : javaในที่นี้ผมจะยกตัวอย่างด้วย Netbeans IDE น่ะครับ<br />
<br />
<div>
1. สร้าง maven project เป็น Java Application<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3qrcBRAeIRDp6LWemf1Lz9__cDHIIMmR2ojG94k-TFRz9Zrhq7JHuqQg8j3zba3ZwnLbKmXM77649h3dqFallWwM47bAY8zJzdgcjlY-I77hUp09N9b-lwF2Rgkjp-y4dzZRsqxIYuct8/s1600/5-25-2015+3-56-29+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="350" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3qrcBRAeIRDp6LWemf1Lz9__cDHIIMmR2ojG94k-TFRz9Zrhq7JHuqQg8j3zba3ZwnLbKmXM77649h3dqFallWwM47bAY8zJzdgcjlY-I77hUp09N9b-lwF2Rgkjp-y4dzZRsqxIYuct8/s640/5-25-2015+3-56-29+PM.png" width="640" /></a></div>
<br />
<a name='more'></a>Maven --> Java Application<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnX6yMV6H6gE7Q4-t3PZUJ24_7SXwAr6UXklw03S5rvPkURB5iVRNRVVNl94rr7kHUYGVjesVrLO3hMwngKWISQGLR32-vX0SL_gBpVY3az5_Tz1U2mioEZY6wN29chLhylKgp_MOVO9FO/s1600/5-25-2015+3-57-27+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="536" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnX6yMV6H6gE7Q4-t3PZUJ24_7SXwAr6UXklw03S5rvPkURB5iVRNRVVNl94rr7kHUYGVjesVrLO3hMwngKWISQGLR32-vX0SL_gBpVY3az5_Tz1U2mioEZY6wN29chLhylKgp_MOVO9FO/s640/5-25-2015+3-57-27+PM.png" width="640" /></a></div>
<br />
ตั้งชื่อ project กำหนด location, GroupId, Version และ Package<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAkquUXF-ELWt1jh9RuYmtJKqnTX3Izv8q_uEigiGdHyGpI2uZHII9zGz_p8tLb6vSgI2un_nWpURws298Q8IsP4Z0PZnwPPQcAMqBfim2WTj25LKtJtLjtTPuMb_iF2kKN6YA1BLenYux/s1600/5-25-2015+3-58-40+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="536" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAkquUXF-ELWt1jh9RuYmtJKqnTX3Izv8q_uEigiGdHyGpI2uZHII9zGz_p8tLb6vSgI2un_nWpURws298Q8IsP4Z0PZnwPPQcAMqBfim2WTj25LKtJtLjtTPuMb_iF2kKN6YA1BLenYux/s640/5-25-2015+3-58-40+PM.png" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
2. สร้าง main class ที่จะ run program<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaE3QUv_TpH1tBhtTTWzR7f0bvsrqUziriJSjo_cldajPEDhVeZ6Fur114zfH1UP2LxYxejs1SDrlm7ga1tB_ZmlK0qNo682izAYl2ENCJbMv1C7gp14lGSkYk-q_VebLv3uUa0P5EoRQZ/s1600/5-25-2015+3-59-12+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="528" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaE3QUv_TpH1tBhtTTWzR7f0bvsrqUziriJSjo_cldajPEDhVeZ6Fur114zfH1UP2LxYxejs1SDrlm7ga1tB_ZmlK0qNo682izAYl2ENCJbMv1C7gp14lGSkYk-q_VebLv3uUa0P5EoRQZ/s640/5-25-2015+3-59-12+PM.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8cLpmtjznUcI1wq3ewCguNdcrPrYZPKAYMoUXdxZsFieTogQv4Phl6DySuZmof2u-5UIWQK0hT4VejG62IzzgYFO8aAcESF-QjW5LvGrJGE_RPwGu0mAcxUfOZ6OYhQrM0eHawuCADj8A/s1600/5-25-2015+3-59-49+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="452" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8cLpmtjznUcI1wq3ewCguNdcrPrYZPKAYMoUXdxZsFieTogQv4Phl6DySuZmof2u-5UIWQK0hT4VejG62IzzgYFO8aAcESF-QjW5LvGrJGE_RPwGu0mAcxUfOZ6OYhQrM0eHawuCADj8A/s640/5-25-2015+3-59-49+PM.png" width="640" /></a></div>
<br />
เขียน code ในที่นี้ให้ show message dialog ง่ายๆ ที่มีคำว่า Hello World!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih81MLE8VRjJDDoE7806eHIrIX4DORulWePbbGIAQEkVIaASGoNuX0NHB3Z3PjQQBqH9szh9a6zbqpW7TzFD4Km-SAqtzEvADGJ1CNU3ac0kW63B4-JgbzlJvoDn3kyOd3D8yNhCXqhG9Q/s1600/5-25-2015+4-04-30+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="368" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih81MLE8VRjJDDoE7806eHIrIX4DORulWePbbGIAQEkVIaASGoNuX0NHB3Z3PjQQBqH9szh9a6zbqpW7TzFD4Km-SAqtzEvADGJ1CNU3ac0kW63B4-JgbzlJvoDn3kyOd3D8yNhCXqhG9Q/s640/5-25-2015+4-04-30+PM.png" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
3. config pom.xml โดยการเพิ่ม plugins สำหรับ pack jar file ลงไป<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGSlHMmZ50djB4f-x3J29hqzDHFqSblhd23Bu0PzLSDlwMC3jB4xQTd0N7JnrJwW-nt5HVkLQtmhn35WM72mLYvUxGI2hsX8C8WTMZLYHyGoBvgHRsDO7NAIBK5_9y9JSK_l9hDgC64WFC/s1600/5-25-2015+4-04-54+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="290" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGSlHMmZ50djB4f-x3J29hqzDHFqSblhd23Bu0PzLSDlwMC3jB4xQTd0N7JnrJwW-nt5HVkLQtmhn35WM72mLYvUxGI2hsX8C8WTMZLYHyGoBvgHRsDO7NAIBK5_9y9JSK_l9hDgC64WFC/s640/5-25-2015+4-04-54+PM.png" width="640" /></a></div>
<br />
<u><span style="color: red;">***** อย่าลืมกำหนด main class ด้วย (สังเกตบรรทัดที่ 29 ในรูป)</span></u><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjq1LLMIEzaundBKX3yQ9SB75nOx5GHkVGMKAKsSFZ6v2nOaQ8ugsZ-K-CV-qgb4vG7zm1-H2F-Ms_nUovua9bv67YOEGX4S0PeoYdGxYj9KVf9cKPRg6MUgsvAhDGGqXZOwYAje8xeh7H/s1600/5-25-2015+4-05-55+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="428" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjq1LLMIEzaundBKX3yQ9SB75nOx5GHkVGMKAKsSFZ6v2nOaQ8ugsZ-K-CV-qgb4vG7zm1-H2F-Ms_nUovua9bv67YOEGX4S0PeoYdGxYj9KVf9cKPRg6MUgsvAhDGGqXZOwYAje8xeh7H/s640/5-25-2015+4-05-55+PM.png" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
<pre class="brush : xml"><project>
....
....
....
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.pamarin.runjar.App</mainClass> <!-- your main class -->
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
</pre>
4. Clean and Build project 1 ครั้ง เพื่อ load dependencies และ pack jar<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTL-IP0Qu0_tk67Cg7za-Kz1Ub-1Re3F1xJq7D62li5eRaBW_RM0PX6xVoCzH6nof_w068ZIcRMnEPNJcPghyphenhyphenikFQMsLFHlrtHiluYBdAD4NBc5VYekzV43NpXDuG1THmo82RY6OYsTp9L/s1600/5-25-2015+4-06-08+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="494" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTL-IP0Qu0_tk67Cg7za-Kz1Ub-1Re3F1xJq7D62li5eRaBW_RM0PX6xVoCzH6nof_w068ZIcRMnEPNJcPghyphenhyphenikFQMsLFHlrtHiluYBdAD4NBc5VYekzV43NpXDuG1THmo82RY6OYsTp9L/s640/5-25-2015+4-06-08+PM.png" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
5. เข้าไปที่ project directory<span style="color: red;"><u>/target</u></span> แล้ว double click .jar file เพื่อ run program</div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIn_s2_Tqo-ZupV5hD_KOzzXpk4kWFuvx6Lta420mIvfrjGQTd_dLochlgvKIGB0vkm7egRndO__Ax4_ZDHxhtKJwDokbIG8in4JEWq1i-kWXBNIcZ2NJGiyxq57Gp7eFFNqRNpmNxF6Ua/s1600/5-25-2015+4-07-39+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIn_s2_Tqo-ZupV5hD_KOzzXpk4kWFuvx6Lta420mIvfrjGQTd_dLochlgvKIGB0vkm7egRndO__Ax4_ZDHxhtKJwDokbIG8in4JEWq1i-kWXBNIcZ2NJGiyxq57Gp7eFFNqRNpmNxF6Ua/s640/5-25-2015+4-07-39+PM.png" width="640" /></a></div>
<div style="text-align: left;">
<br />
หวังว่าจะเป็นประโยชน์สำหรับทุกคนที่เข้ามาอ่านน่ะครับ<br />
<br />
# ขออภัยสำหรับรูปภาพด้วย เนื่องจากจอผมเป็นจอความละเอียดสูง ตัวหนังสือบางตัวอาจจะดูค่อนข้างเล็กครับ :)</div>
</div>
jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com3tag:blogger.com,1999:blog-3030476649588243638.post-86153692086794996602015-05-24T02:28:00.002-07:002015-05-24T04:44:59.972-07:00ฝึก recursive ด้วยโจทย์ความน่าจะเป็น<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBgTyFK-Ee8ZzVNdReBl2f7W2IkvpSP0zFmcaSH6wzwZNfogkqRIz8ulMMeK-M__byz8VFV7JdGdc6I221L3Rh1FkN68rBYQ-gckiRHYGfcMIBbnRhYksoZ_YJ6ZGB8taoDLKZCF7pwkC0/s1600/2000px-Dice_Distribution_%2528bar%2529.svg.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBgTyFK-Ee8ZzVNdReBl2f7W2IkvpSP0zFmcaSH6wzwZNfogkqRIz8ulMMeK-M__byz8VFV7JdGdc6I221L3Rh1FkN68rBYQ-gckiRHYGfcMIBbnRhYksoZ_YJ6ZGB8taoDLKZCF7pwkC0/s400/2000px-Dice_Distribution_%2528bar%2529.svg.png" style="border: none;" width="400" /></a></div>
<div style="text-align: center;">
<br /></div>
โจทย์นี้เป็นโจทย์ที่ผมใช้ฝึกวิธีคิด การแก้ปัญหา ซึ่งอาจมีคนคิด หรือสร้าง tools สำเร็จรูปเอาไว้แล้ว บางคนมาเห็นโพสต์นี้ อาจจะคิดว่าทำไมไม่ใช้เครื่องมือที่คนอื่นทำไว้แล้วล่ะ จะมามัวเสียเวลาสร้างเองทำไม ของคนอื่นอาจจะดีกว่าด้วยซ้ำ<br />
<br />
ครับ!!! ผมเคยได้ยินประโยคแบบนี้มานักต่อนักล่ะ บางคนบอกว่าผมไม่รู้จักใช้เครื่องมือให้เกิดประโยชน์<br />
คุณกำลังเข้าใจผิดกับคำว่า "การเลือกใช้เครื่องมือ" กับ "การฝึกทักษะการแก้ปัญหา"<br />
ที่ผมเขียนเอง เพราะผมต้องการฝึก skill เพื่อให้เกิดความคล่องตัว เมื่อเราเจอโจทย์ปัญหาลักษณะนี้ เราจะแก้มันได้รึเปล่า ลองใช้ความคิดของเราดูสิ<br />
skill มันส่งต่อผ่านประสบการณ์กันไม่ได้ ต่อให้มีคนพยายามเล่าประสบการณ์ทั้งชีวิตของเขาให้คุณฟัง คุณก็ทำแบบเขาไม่ได้ คุณต้องฝึก ฝึก ฝึก ลงมือทำด้วยตัวคุณเองครับ<br />
<br />
<b><span style="font-size: x-large;">ปัญหา</span></b><br />
<br />
โดยทั่วไป การหาความน่าจะเป็นหรือความเป็นไปได้ของสมาชิกทั้งหมดภายในเซต S ซึ่งมีสมาชิกจำนวน n ตัว<br />
คุณจะต้องวนลูปทั้งหมด n รอบ และลึก n ชั้น ถึงจะได้ค่าทั้งหมดออกมา เช่น<br />
<br />
S = {'A', 'B', 'C'}<br />
n = 3 ตัว<br />
จำนวนความเป็นไปได้ทั้งหมดคือ 3 x 3 x 3 = 27 ค่า<br />
<br />
การเขียนโปรแกรมแบบง่ายๆ จะได้
<br />
<pre class="brush : js">for (var i = 0; i < S.lenght; i++){
for (var j = 0; j < S.lenght; j++){
for (var k = 0; k < S.lenght; k++){
print(S[i] + ',' + S[j] + ',' + S[k]);
}
}
}
A,A,A
A,A,B
A,A,C
A,B,A
A,B,B
...
...
...
C,B,C
C,C,A
C,C,B
C,C,C
</pre>
<u>แล้วถ้าเขาต้องการให้คุณเขียน code ที่รองรับ S ที่มี size ขนาดใดก็ได้ คุณจะเขียนยังไง?</u><br />
แน่นอน loop ใช้ไม่ได้ล่ะ เพราะมัน fixed ตายตัว<br />
แล้วอะไรที่เราเอามาใช้แทน loop ได้ แถมยังเป็นแบบ dynamic อีก <br />
<br />
<b><span style="font-size: large;">คำตอบคือ Recursive ครับ</span></b><br />
<a name='more'></a><br />
code<br />
<pre class="brush : js">function probabilityWalking(topElement, elements, index, callback){
if (index < 0) {
callback(topElement);
return;
}
for (var i=0; i < elements.length; i++) {
probabilityWalking(
(topElement ? (topElement + ',') : topElement) + elements[i],
elements,
index - 1,
callback
);
}
}
</pre>
using
<br />
<pre class="brush : js">probabilityWalking('', ['A', 'B'], 1, function(el){
console.log(el);
});
A,A
A,B
B,A
B,B
</pre>
<pre class="brush : js">probabilityWalking('', ['A', 'B', 'C'], 2, function(el){
console.log(el);
});
A,A,A
A,A,B
A,A,C
A,B,A
A,B,B
...
...
...
C,B,C
C,C,A
C,C,B
C,C,C
</pre>
ตอนนี้ code เราสามารถหาความเป็นไปได้ทั้งหมดของ S ขนาดเท่าใดก็ได้ แล้วครับ :)<br />
<br />
<span style="font-size: x-large;"><b>การนำแนวคิดไปประยุกต์ใช้</b></span><br />
maven dependencies
<br />
<pre class="brush : xml"><dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</pre>
Probability.java
<br />
<pre class="brush : java">package com.blogspot.na5cent.learning;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
/**
* @author redcrow
*/
public class Probability {
private static interface Callback {
void call(String element);
}
private final List<String> elements;
private boolean distinct = false;
private boolean unique = false;
private Integer size;
private Set<String> signatures;
private Probability(List<String> elements) {
this.elements = elements;
}
public static Probability ofElements(String... elements) {
return ofElements(Arrays.asList(elements));
}
public static Probability ofElements(List<String> elements) {
return new Probability(elements);
}
public Probability size(int size) {
this.size = size;
return this;
}
public Probability distinct() {
this.distinct = true;
return this;
}
public Probability unique() {
this.unique = true;
return this;
}
private Set<String> getSignatures() {
if (signatures == null) {
signatures = new HashSet<>();
}
return signatures;
}
private boolean contains(String str, String target) {
if (!str.isEmpty()) {
String[] els = StringUtils.split(str, ":");
for (String el : els) {
if (el.equals(target)) {
return true;
}
}
}
return false;
}
private String findSignature(String str) {
String[] els = StringUtils.split(str, ":");
Arrays.sort(els);
return StringUtils.join(els, ":");
}
private boolean has(String element) {
String signature = findSignature(element);
if (getSignatures().contains(signature)) {
return true;
}
getSignatures().add(signature);
return false;
}
private void walking(String topElement, List<String> elements, int index, Callback callback) {
if (index < 0) {
if (unique && has(topElement)) {
return;
}
callback.call(topElement);
return;
}
for (String element : elements) {
if (distinct && contains(topElement, element)) {
continue;
}
walking(
(topElement.isEmpty() ? topElement : topElement + ":") + element,
elements,
index - 1,
callback
);
}
}
public List<String> find() {
if (size == null) {
size = elements.size();
}
final List<String> results = new ArrayList<>();
walking("", elements, size - 1, new Callback() {
@Override
public void call(String element) {
results.add(element);
}
});
return results;
}
}
</pre>
<br />
ลองดูวิธีการเรียกใช้และผลลัพธ์ที่ Test<br />
<br />
ProbabilityTest.java
<br />
<pre class="brush : java">package com.blogspot.na5cent.learning;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* @author redcrow
*/
public class ProbabilityTest {
@Test
public void allOf2ElementsSize2() {
List<String> probs = Probability
.ofElements("A", "B")
.find();
List<String> results = Arrays.asList(
"A:A",
"A:B",
"B:A",
"B:B"
);
assertEquals(probs, results);
}
@Test
public void allOf3ElementsSize3() {
List<String> probs = Probability
.ofElements("A", "B", "C")
.find();
List<String> results = Arrays.asList(
"A:A:A",
"A:A:B",
"A:A:C",
"A:B:A",
"A:B:B",
"A:B:C",
"A:C:A",
"A:C:B",
"A:C:C",
"B:A:A",
"B:A:B",
"B:A:C",
"B:B:A",
"B:B:B",
"B:B:C",
"B:C:A",
"B:C:B",
"B:C:C",
"C:A:A",
"C:A:B",
"C:A:C",
"C:B:A",
"C:B:B",
"C:B:C",
"C:C:A",
"C:C:B",
"C:C:C"
);
assertEquals(probs, results);
}
@Test
public void distinctOf2ElementsSize2() {
List<String> probs = Probability
.ofElements("A", "B")
.distinct() //ตัด A:A กับ B:B ออก เพราะ A กับ B ซ้ำกัน 2 ตัว
.find();
List<String> results = Arrays.asList(
"A:B",
"B:A"
);
assertEquals(probs, results);
}
@Test
public void distinctOf3ElementsSize3() {
List<String> probs = Probability
.ofElements("A", "B", "C")
.distinct()
.find();
List<String> results = Arrays.asList(
"A:B:C",
"A:C:B",
"B:A:C",
"B:C:A",
"C:A:B",
"C:B:A"
);
assertEquals(probs, results);
}
@Test
public void distinctOf4ElementsSize2() {
List<String> probs = Probability
.ofElements("A", "B", "C", "D")
.distinct()
.size(2)
.find();
List<String> results = Arrays.asList(
"A:B",
"A:C",
"A:D",
"B:A",
"B:C",
"B:D",
"C:A",
"C:B",
"C:D",
"D:A",
"D:B",
"D:C"
);
assertEquals(probs, results);
}
@Test
public void uniqueOf2ElementsSize2() {
List<String> probs = Probability
.ofElements("A", "B")
.unique()
.find();
List<String> results = Arrays.asList(
"A:A",
"A:B",
"B:B"
);
assertEquals(probs, results);
}
@Test
public void uniqueOf3ElementsSize3() {
List<String> probs = Probability
.ofElements("A", "B", "C")
.unique()
.find();
List<String> results = Arrays.asList(
"A:A:A",
"A:A:B",
"A:A:C",
"A:B:B",
"A:B:C",
"A:C:C",
"B:B:B",
"B:B:C",
"B:C:C",
"C:C:C"
);
assertEquals(probs, results);
}
@Test
public void uniqueAndDistinctOf2ElementsSize2() {
List<String> probs = Probability
.ofElements("A", "B")
.distinct() //ตัด A:A กับ B:B ออก เพราะ A กับ B ซ้ำกัน 2 ตัว
.unique() //A:B กับ B:A ถือเป็นตัวเดียวกัน แค่สลับตำแหน่ง
.find();
List<String> results = Arrays.asList(
"A:B"
);
assertEquals(probs, results);
}
@Test
public void uniqueAndDistinctOf3ElementsSize3() {
List<String> probs = Probability
.ofElements("A", "B", "C")
.distinct()
.unique()
.find();
List<String> results = Arrays.asList(
"A:B:C"
);
assertEquals(probs, results);
}
@Test
public void uniqueAndDistinctOf3ElementsSize2() {
List<String> probs = Probability
.ofElements("A", "B", "C")
.distinct()
.unique()
.size(2)
.find();
List<String> results = Arrays.asList(
"A:B",
"A:C",
"B:C"
);
assertEquals(probs, results);
}
@Test
public void uniqueAndDistinctOf4ElementsSize2() {
List<String> probs = Probability
.ofElements("A", "B", "C", "D")
.distinct()
.unique()
.size(2)
.find();
List<String> results = Arrays.asList(
"A:B",
"A:C",
"A:D",
"B:C",
"B:D",
"C:D"
);
assertEquals(probs, results);
}
@Test
public void uniqueAndDistinctOf4ElementsSize3() {
List<String> probs = Probability
.ofElements("A", "B", "C", "D")
.distinct()
.unique()
.size(3)
.find();
List<String> results = Arrays.asList(
"A:B:C",
"A:B:D",
"A:C:D",
"B:C:D"
);
assertEquals(probs, results);
}
}
</pre>
code นี้เป็นส่วนนึงของโจทย์ที่ผมกำลังแก้อยู่ครับ โจทย์มีอยู่ว่า
<br />
<br />
ให้เขียนโปรแกรมเพื่อหาสมการทั้งหมดที่เป็นไปได้ โดยให้เรา<br />
1. ใส่คำตอบที่อยากจะได้ลงไป<br />
2. ใส่ตัวเลขอะไรก็ได้ กี่ตัวก็ได้ลงไป (ในที่นี้คือ S)<br />
3. ใส่ operators + หรือ - หรือ * หรือ / ลงไปกี่ตัวก็ได้ ใน 4 ตัวนี้<br />
แล้วให้โปรแกรมคืนค่าสมการที่เกิดจากการป้อน input ข้อ 1- 3 ทั้งหมดออกมาครับ<br />
<br />
น่าสนุกน่ะ <br />
ตอนนี้ผมเหลือเงื่อนไขวงเล็บทั้งหมดที่เป็นไปได้ ตามจำนวนสมาชิกใน S ครับ<br />
ยังคิดไม่ออก ฮ่าๆๆๆ<br />
<br />jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com1tag:blogger.com,1999:blog-3030476649588243638.post-79138094198084530172015-04-18T09:05:00.003-07:002015-04-19T20:18:35.852-07:00angularjs enumeration : javascriptEnum Factory
<pre class="brush : js">
var mapApp = ...
myApp.factory('Enumerate', function () {
return function (list, fnc) {
var Enumerate = function (ctx) {
if (angular.isObject(ctx)) {
angular.forEach(ctx, function (val, key) {
this[key] = val;
}, this);
} else if (angular.isString(ctx)) {
this.name = ctx;
} else {
throw new Error('incorrect arguments.');
}
};
Enumerate.values = [];
Enumerate.valueOf = function (ctx) {
var obj = null;
angular.forEach(Enumerate.values, function (item) {
if (item.name === ctx) {
obj = item;
return false;
}
});
if (!obj) {
obj = new Enumerate(ctx);
Enumerate.values.push(obj);
}
return obj;
}
Enumerate.prototype.toString = function () {
return this.name;
};
fnc && angular.forEach(fnc, function (val, key) {
Enumerate.prototype[key] = val;
});
angular.forEach(list, function (item) {
Enumerate[
angular.isObject(item)
? item.name
: item
] = Enumerate.valueOf(item);
});
return Enumerate;
};
});
</pre>
Using
<pre class="brush : js">
myApp..factory('StandardColumn', [
'Enumerate',
function (Enumerate) {
var StandardColumn = function () {
};
/* define enum */
StandardColumn.Format = Enumerate([
{name: 'STRING', description: 'ตัวหนังสือ'},
{name: 'NUMBER', description: 'ตัวเลข'},
{name: 'STUDENT', description: 'นักเรียน'}
]);
StandardColumn.Format.fromDescription = function (desc) {
var obj = null;
angular.forEach(StandardColumn.Format.values, function (item) {
if (item.description === desc) {
obj = item;
return false;
}
})
return obj;
};
StandardColumn.Operation = Enumerate([
'SUM',
'AVERAGE'
]);
StandardColumn.prototype = {
id: null,
name: null,
sequence: 1,
format: StandardColumn.Format.STRING,
operation: null,
operateWith: null
};
return StandardColumn;
}
]);
</pre>jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-89369778600985641472015-03-09T18:16:00.002-07:002015-03-09T18:18:16.999-07:00พีระมิต (pyramid) : javaวันนี้นั่งแย่งโจทย์น้องฝึกงานเล่นครับ
<br />
<pre class="brush : java">package com.blogspot.na5cent.learning;
import java.util.Scanner;
/**
* @author redcrow
*/
public class Pyramid {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int size = n * (n + 1) / 2;
int bigSpace = (n - 1) / 2;
for (int i = 1; i <= size; i++) {
int numb = i % n;
int space = bigSpace - i / n;
boolean star = numb > space && numb <= (n - space);
System.out.print(star ? "*" : " ");
System.out.print(numb == 0 ? "\n" : "");
}
}
}
</pre>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihn1Y8Zfi956CCoejMBwc_ygxat4nPnTJo5B5284NKwDB9jkuHtLgYg-G16l-zKwEUTP0e-Ks6e7lJazNaDNIw97aF-tUd83eUHHNkyEywOEWL3J3CZhDyHmfAX09SMSh4wSS7LplD7oJ3/s1600/3-9-2015+7-35-23+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihn1Y8Zfi956CCoejMBwc_ygxat4nPnTJo5B5284NKwDB9jkuHtLgYg-G16l-zKwEUTP0e-Ks6e7lJazNaDNIw97aF-tUd83eUHHNkyEywOEWL3J3CZhDyHmfAX09SMSh4wSS7LplD7oJ3/s1600/3-9-2015+7-35-23+PM.png" height="448" width="640" /></a></div>
jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-91159044084940867242015-03-04T02:45:00.005-08:002015-03-04T03:20:04.178-08:00Spring security bypass authen : java<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXnIP6uibYGk3swdsByg3_Zgg1hKj4kK85jt0QPr6ZTvlL6XOvoHi8uzLHvIrnSMmdglJITRHpU_ApEdpMog2TgiaGVPvTua1xux7m1uLke5azhElJK9WdjD6ThptmKhlTuiwaMP-FrbaA/s1600/3-4-2015+6-11-06+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXnIP6uibYGk3swdsByg3_Zgg1hKj4kK85jt0QPr6ZTvlL6XOvoHi8uzLHvIrnSMmdglJITRHpU_ApEdpMog2TgiaGVPvTua1xux7m1uLke5azhElJK9WdjD6ThptmKhlTuiwaMP-FrbaA/s1600/3-4-2015+6-11-06+PM.png" height="278" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
บางครั้งเราต้องการให้ระบบ bypass authen (กระโดดข้ามการ authen) ด้วย username password<br />
เราสามารถทำแบบนี้ได้ครับ spring จะไม่ check username password เรา<br />
เราแค่จับ ข้อมูล user (user details) ยัดเข้าไปใน spring context security เลย<br />
<br />
<span style="font-size: large;"><b>ทำไมต้อง bypass</b></span><br />
<br />
เนื่องจากการ authen แบบเดิม เราต้องรู้ username และ clean password เพื่อ authen เข้าใช้งาน<br />
มันเกิดปัญหาว่า ระบบที่ผมทำ ผมไม่ได้เก็บ password ของผู้ใช้เป็น clean text ไว้ ผมเข้ารหัส password ไว้ตั้งแต่แรก (เพื่อความปลอดภัย) เพราะฉะนั้น ผมจึงใช้วิธี authen แบบเดิมไม่ได้ (authen แบบเดิม จะเอา password มา encrypt check กับ encrypt password ที่มีอยู่ ว่า matches กันมั้ย)<br />
<br />
ปัญหาต่อมา คือ ผมทำระบบที่สามารถ login ด้วยบัญชีผู้ใช้ facebook แน่นอนว่ามันไม่มี password เพราะ facebook ใช้ token แล้วผมจะ authen เข้าสู่ระบบที่ผมทำยังไง?<br />
<br />
คิดอยู่สักพัก ก็เกิดคำถามขึ้นมาในหัว เราสามารถ bypass มันได้มั้ย ? <br />
ก็เลยเป็นที่มาของบทความวันนี้ครับ<br />
<a name='more'></a><br />
User.java
<br />
<pre class="brush : java">...
...
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class User implements UserDetails {
....
....
}
</pre>
BypassAuthen.java
<br />
<pre class="brush : java">package com.blogspot.na5cent.security;
import com.blogspot.na5cent.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
/**
* @author redcrow
*/
@Component
public class BypassAuthen {
public void login(User user) {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = new UsernamePasswordAuthenticationToken(
user,
null,
user.getAuthorities()
);
securityContext.setAuthentication(authentication);
}
}
</pre>
jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com34tag:blogger.com,1999:blog-3030476649588243638.post-91554224271534514632015-02-03T01:19:00.000-08:002015-02-03T01:51:31.092-08:00oracle sql order by multiple point string <span style="font-size: x-large;"><b>ปัญหา</b></span><br />
<br />
ถ้าเรา order by string แบบธรรมดาๆ เราจะเจอปัญหาแบบนี้<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUh8KsWyA1Pok_4Rgkw7srGXj-etbrjekiFsfqHmtBBQSV8qK2CxpGQFGA30DTsD2ULdrCkRtKgF-wl2vY2_MZEDk8FVVociJFfcv5lNnq11Y1pODnGjyd7NFQaueim3-wJ-0me0LMANPc/s1600/2-3-2015+4-16-32+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUh8KsWyA1Pok_4Rgkw7srGXj-etbrjekiFsfqHmtBBQSV8qK2CxpGQFGA30DTsD2ULdrCkRtKgF-wl2vY2_MZEDk8FVVociJFfcv5lNnq11Y1pODnGjyd7NFQaueim3-wJ-0me0LMANPc/s1600/2-3-2015+4-16-32+PM.png" height="640" width="446" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<div style="text-align: left;">
การ order by string 10 จะมาก่อน 2 เพราะรหัส ASCII ของ 1 อยู่ก่อน 2</div>
<div style="text-align: left;">
<br />
<b><span style="font-size: x-large;">วิธีแก้</span></b><br />
<br />
เปลี่ยนมา order by ด้วย sql function ที่ผมเขียนขึ้นมาใหม่ ได้เป็น</div>
<a name='more'></a><br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAPTW3ioYYfXICgVTKDya-7Wm5U5TR20EsHRvpPMD7vo1efM5C8VfGs_5IUq1U7PwbiMPE-Qpp5mhPIxq2PTao6X84f9Lx6dsGI2RS8eIz50Rk3sT2KfUeSG-ryRQh19Vrl5XIef75bVa7/s1600/2-3-2015+4-17-36+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAPTW3ioYYfXICgVTKDya-7Wm5U5TR20EsHRvpPMD7vo1efM5C8VfGs_5IUq1U7PwbiMPE-Qpp5mhPIxq2PTao6X84f9Lx6dsGI2RS8eIz50Rk3sT2KfUeSG-ryRQh19Vrl5XIef75bVa7/s1600/2-3-2015+4-17-36+PM.png" height="614" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
การแสดงผล ถูกต้อง เพราะเราแปลงมันไปเป็นตัวเลขก่อน</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
PL/SQL สำหรับแปลง multiple point ไปเป็นตัวเลข</div>
</div>
<pre class="brush : sql">CREATE OR REPLACE PACKAGE converter_pack AS
FUNCTION pad_string(p_multiple_point VARCHAR2, p_index NUMBER) RETURN VARCHAR2;
FUNCTION multiple_point2number(p_multiple_point VARCHAR2, p_point NUMBER) RETURN NUMBER;
END converter_pack;
/* package body */
CREATE OR REPLACE PACKAGE body converter_pack AS
FUNCTION pad_string( p_multiple_point VARCHAR2, p_index NUMBER) RETURN VARCHAR2 AS
l_pad VARCHAR2(255);
BEGIN
SELECT lpad(regexp_substr (p_multiple_point, '[^.]+', 1, p_index), 3, '0')
INTO l_pad
FROM dual;
RETURN l_pad;
END pad_string;
FUNCTION multiple_point2number(p_multiple_point VARCHAR2, p_point NUMBER) RETURN NUMBER AS
l_temp VARCHAR2(255);
l_numb VARCHAR2(255);
BEGIN
FOR i IN 1..p_point LOOP
l_temp := pad_string(p_multiple_point, i);
IF l_temp = '' OR l_temp IS NULL THEN
l_temp := '000';
END IF;
l_numb := l_numb || l_temp;
END LOOP;
RETURN to_number(l_numb);
END multiple_point2number;
END converter_pack;
</pre>
การเรียกใช้
<br />
<pre class="brush : sql">select SEQUENCE_ORDER,
converter_pack.multiple_point2number(SEQUENCE_ORDER, 5) as numb
from MY_TABLE
order by numb
</pre>
multiple_point2number(p_multiple_point VARCHAR2, p_point NUMBER)
<br />
<br />
p_multiple_point คือ String multiple point ที่เราส่งเข้าไป เช่น 1.2.3.4<br />
p_point คือ จำนวนจุด (.) สูงสุด ที่คิดว่าจะมีใน p_multiple_point ซึ่งของผมคือ 5 (เช่น 1.2.3.4.5.6)jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com2tag:blogger.com,1999:blog-3030476649588243638.post-21611409422263155792015-02-01T09:46:00.000-08:002020-01-17T00:26:53.926-08:00ทำความรู้จักกับ JWT (Json Web Token)ย้ายบทความไปไว้ที่ <a href="https://www.jittagornp.me/blog/what-is-jwt/">https://www.jittagornp.me/blog/what-is-jwt/</a>jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com10tag:blogger.com,1999:blog-3030476649588243638.post-40846836549419693042015-01-29T21:42:00.001-08:002015-01-29T21:42:33.973-08:00Oracle SQL แยกหัวข้อ ออกจากข้อความ<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEip6JqY-JaUXF5urXDI_9dARN7-lL-lx5R4pL2T6GwgOLXwqvrPUd2ZwTYPO7vgvQYuvOrM_l0OwZP2YoApmYRx2kSx5K2W5Jcc9QnmQlR7E4maijvHdIaon85Lyvp7gOfwLYV0y8oQ9_Ii/s1600/1-30-2015+12-06-21+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEip6JqY-JaUXF5urXDI_9dARN7-lL-lx5R4pL2T6GwgOLXwqvrPUd2ZwTYPO7vgvQYuvOrM_l0OwZP2YoApmYRx2kSx5K2W5Jcc9QnmQlR7E4maijvHdIaon85Lyvp7gOfwLYV0y8oQ9_Ii/s1600/1-30-2015+12-06-21+PM.png" height="466" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
code
<br />
<pre class="brush : sql">select data.*,
trim(replace(data.old_name, (numb), '')) new_name
from(
select name old_name,
regexp_substr(name, '^[0-9ก-ฮ\.]+\)*\s') numb
from MY_TABLE
) data
</pre>
jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-3637284142425678902015-01-27T20:00:00.002-08:002020-01-12T06:10:04.116-08:00ทำความรู้จักกับ Java Stream<span style="background-color: white; color: #666666; font-family: "Trebuchet MS", Trebuchet, sans-serif; font-size: 13px;">ทำการย้ายบทความไปไว้ที่ </span><br />
<div style="background-color: white; color: #666666; filter: initial !important; font-family: "Trebuchet MS", Trebuchet, sans-serif; font-size: 13px;">
<br style="filter: initial !important;" /><div style="filter: initial !important;">
<a href="https://www.jittagornp.me/blog/java-io-stream/">https://www.jittagornp.me/blog/java-io-stream/</a></div>
</div>
jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com5tag:blogger.com,1999:blog-3030476649588243638.post-74007104848756827282015-01-21T20:27:00.001-08:002015-01-21T21:36:25.029-08:00แก้ปัญหา multiple values sql IN : oracleคำสั่ง IN เป็นคำสั่งที่ใช้สำหรับตรวจสอบว่า ค่าที่เลือกมานั้น มีอยู่ใน IN หรือไม่ เช่น
<br />
<pre class="brush : sql">...
...
SELECT code
FROM ...
WHERE code IN('AAA', 'BBB', 'CCC')
...
...
</pre>
ซึงเราสามารถตรวจสอบได้เพียง ครั้งละ 1 ค่าเท่านั้น (single value)<br />
ในที่นี้ field <i><span style="color: #6aa84f;">code</span></i> สามารถเป็น <span style="color: #3d85c6;">AAA </span>หรือ <span style="color: #3d85c6;">BBB</span> หรือ <span style="color: #3d85c6;">CCC</span> ได้เท่านั้น (ถึงจะ match กัน)<br />
ไม่สามารถเป็น multiple values เช่น <span style="color: #3d85c6;">'AAA, BBB' </span>หรือ <span style="color: #3d85c6;">'BBB, CCC'</span> ได้ เพราะมันจะไม่ตรงกับตัวไหนใน IN เลย<br />
<br />
<span style="color: #3d85c6;"> 'AAA, BBB' </span>ไม่ match กับ <span style="color: #3d85c6;">'AAA'</span><br />
<span style="color: #3d85c6;"> 'AAA, BBB'</span> ไม่ match กับ<span style="color: #3d85c6;"> 'BBB'</span><br />
<span style="color: #3d85c6;"> 'AAA, BBB'</span> ไม่ match กับ <span style="color: #3d85c6;">'CCC'</span><br />
<br />
แล้วถ้าเราต้องการให้มัน match กันละ เราจะทำยังไง?<br />
เพราะตามความเป็นจริงแล้ว <span style="color: #3d85c6;"> 'AAA, BBB' </span>มันควรจะ match กับทั้ง <span style="color: #3d85c6;">'AAA'</span> และ<span style="color: #3d85c6;"> 'BBB'</span><br />
<br />
<span style="font-size: large;"><b>มาดูแนวคิด <complete id="goog_1097330214">+ </complete>วิธีแก้ของผมกันครับ</b></span><br />
<a name='more'></a><br />
sql IN เป็นคำสั่งที่ใช้ข้อมูลในแต่ละ rows มา IN<br />
ฉะนั้น เราต้องแปลง 'AAA, BBB' ของเราให้ไปเป็น rows ก่อน จากบทความนี้ครับ <a href="http://na5cent.blogspot.com/2015/01/split-string-to-rows-oracle-sql.html">Split String to rows : Oracle SQL</a> ได้ผลลัพดังนี้<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV21yQ6Woz5l-pEC8kYfUSttlYWi-d8IZqL7D1wkJAzN-w8Y0WayXyH_iIYFNYaJRDdOuT2LrmLeTpv-NpNBtUDrUiaib1xsJV29z2ZHOzgukd9CyGh9j9tYZvjN5RM7ZXM__2VlCL9EAT/s1600/1-22-2015+11-42-48+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV21yQ6Woz5l-pEC8kYfUSttlYWi-d8IZqL7D1wkJAzN-w8Y0WayXyH_iIYFNYaJRDdOuT2LrmLeTpv-NpNBtUDrUiaib1xsJV29z2ZHOzgukd9CyGh9j9tYZvjN5RM7ZXM__2VlCL9EAT/s1600/1-22-2015+11-42-48+AM.png" height="322" width="640" /></a></div>
<br />
พอเราได้ rows มาแล้ว เราก็สามารถใช้ sql IN ได้ <br />
แต่! คำตอบที่ได้ออกมา มันเป็น การลด rows ลงเฉยๆ <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYH1J5ULqYl9hLGRNmddPmq-VZTlCXvsGh9Yix_xhn4jQ6Dex8JxegWbczuMzSe9RyI3RLr9ARP68T1LaPFXQYoqQ5aclr9F0wQtUz0XPua3RzRCovNBvvYS3Oq8bSGSYxcU7YoLyLY7KK/s1600/1-22-2015+11-49-09+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYH1J5ULqYl9hLGRNmddPmq-VZTlCXvsGh9Yix_xhn4jQ6Dex8JxegWbczuMzSe9RyI3RLr9ARP68T1LaPFXQYoqQ5aclr9F0wQtUz0XPua3RzRCovNBvvYS3Oq8bSGSYxcU7YoLyLY7KK/s1600/1-22-2015+11-49-09+AM.png" height="368" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
จริงๆ ผลลัพธ์ที่เราต้องการคือ ตอบว่า match หรือไม่ match เท่านั้น (ตอบเป็น boolean หรือ 1 กับ 0)<br />
เราจึงครอบมันด้วยการ count แล้วเช็คว่าถ้า count แล้วมากกว่า 0 ก็แสดงว่า match (ตอบ 1) แต่ถ้าเท่ากับ 0 ก็คือไม่ match (ตอบ 0)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjmdXI6WI4BkMkSDVp02tDO1nxskjwz30_leUaap_ZeGgnvrpvJDrS11QHhq8B6l3ptXK2Gm7wWzXR996XLKfTtzyhvN0jGD9sG3cvYBzp0fm5mfik0ME5tlUiqEacol5kkNHULNfiyx60/s1600/1-22-2015+11-46-43+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjmdXI6WI4BkMkSDVp02tDO1nxskjwz30_leUaap_ZeGgnvrpvJDrS11QHhq8B6l3ptXK2Gm7wWzXR996XLKfTtzyhvN0jGD9sG3cvYBzp0fm5mfik0ME5tlUiqEacol5kkNHULNfiyx60/s1600/1-22-2015+11-46-43+AM.png" height="352" width="640" /></a></div>
<br />
โอเค แนวคิดนี้ใช้ได้ ท่าทางจะ work <br />
<u><b>แต่ก็ยังเจอปัญหาอยู่ดี</b></u><br />
<br />
ตอนนี้ <span style="color: #3d85c6;">'AAA, BBB, CCC' </span>เราเป็น fixed code ถ้าเราต้องการส่งค่านี้มาจากการ select ข้างนอกล่ะ เราจะทำยังไง คำตอบคือ <u><span style="color: red;">ไม่ได้</span></u> เพราะมันมี select count ครอบอยู่ 1 ชั้น ที่กันไม่ให้ select ชั้นนอก ส่ง field select เข้ามาได้ <br />
<br />
<i><b><span style="font-size: x-large;">ชิปหายล่ะ </span></b></i> แนวคิดได้ แต่ดันตายที่ select count กั้นอยู่ จะแก้ปัญหานี้ยังไง?<br />
แนวทางการแก้ปัญหาของผมคือ<br />
<br />
<div style="text-align: center;">
<span style="color: red; font-size: large;">ทำยังไงก็ได้ ให้ select count มันอยู่ชั้นเดียวกับ 'AAA, BBB, CCC' </span></div>
<br />
นั่งคิดอยู่สักพักนึงครับ โอเค ผมเกทล่ะ<br />
<br />
<b><span style="font-size: large;">แนวคิดเดิม แต่เปลี่ยนกรรมวิธีใหม่</span></b><br />
ใช้ procedure (PL/SQL) เข้ามาช่วย รวบคำสั่ง split row to string ไปไว้ใน sql function ดังนี้<br />
(<a href="http://na5cent.blogspot.com/2014/08/define-oracle-plsql-collection-object.html">define oracle pl/sql collection (object and table) types</a>)<br />
<br />
define type
<br />
<pre class="brush :sql">CREATE OR REPLACE TYPE string_list AS TABLE OF varchar2(255);
</pre>
write procedure
<br />
<pre class="brush : sql">CREATE OR REPLACE FUNCTION split_string2list(p_string VARCHAR2) RETURN string_list AS
l_list string_list := string_list();
BEGIN
FOR rcd IN
(
SELECT DISTINCT str --reduce duplicate string
FROM
(
SELECT trim(regexp_substr(p_string, '[^,]+', 1, LEVEL)) str
FROM dual
CONNECT BY LEVEL <= regexp_count(p_string, ',')+1
)
WHERE str IS NOT NULL
)
LOOP
l_list.extend();
l_list(l_list.last) := rcd.str;
END LOOP;
RETURN l_list;
EXCEPTION WHEN OTHERS THEN
RETURN NULL;
END split_string2list;
</pre>
ลองเขียน code select ใหม่<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_EzC2Hjw25FxSkz3cdLqf-2Xi1Q8DkFmmRrGMB8zZC4oohGb2M420nrzZlDPitT8bCMaMrxHtGR7ufzh_3KXfidl9lqaGQaJcFck4w6Jj89M_Q0eXQeq4Ty1LAiRBLnziRRTBmWNo1M6g/s1600/1-22-2015+12-10-54+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_EzC2Hjw25FxSkz3cdLqf-2Xi1Q8DkFmmRrGMB8zZC4oohGb2M420nrzZlDPitT8bCMaMrxHtGR7ufzh_3KXfidl9lqaGQaJcFck4w6Jj89M_Q0eXQeq4Ty1LAiRBLnziRRTBmWNo1M6g/s1600/1-22-2015+12-10-54+PM.png" height="338" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
ได้ล่ะ สั้นๆ </div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7TPXaEIezMl8Iq258hXZY88lP47v1OPei4bAgIttWBBxOhrAbEAGFMiQnoMlag5VfOpVnRPId6pIHoEFLTwa9IVOIGtMeise4Ck9hpaKnmTNGbrjUdbgpbqKlUdFMSppowIK4YxFmGpEj/s1600/1-22-2015+12-12-43+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7TPXaEIezMl8Iq258hXZY88lP47v1OPei4bAgIttWBBxOhrAbEAGFMiQnoMlag5VfOpVnRPId6pIHoEFLTwa9IVOIGtMeise4Ck9hpaKnmTNGbrjUdbgpbqKlUdFMSppowIK4YxFmGpEj/s1600/1-22-2015+12-12-43+PM.png" height="310" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
ตอนนี้ไม่มี select count มาครอบมันล่ะ เพราะ count อยู่ใน LEVEL เดียวกัน โอเค แก้ปัญหาได้ล่ะ</div>
<div style="text-align: left;">
ต่อไป เราก็สามารถส่ง select field จากข้างนอก เข้ามา check ได้ว่า field นี้ match กับที่อยู่ใน IN หรือไม่ โดยสามารถส่งเข้ามาเป็น multiple values ใน column เดียวได้ครับ</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
หวังว่าบทความนี้จะเป็นประโยชน์สำหรับ developer ทุกคนที่เข้ามาอ่านน่ะครับ :)</div>
jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-6057659548361935292015-01-19T04:58:00.000-08:002015-01-19T05:01:05.184-08:00Split String to rows : Oracle SQL<div class="separator" style="clear: both; text-align: center;">
บางครั้งเราอาจเจอโจทย์ที่ต้องแยก String ออกเป็นหลายๆ rows ครับ</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6bXTrkYEmV_724EGNhVYTi3C6VvEGSmz4NQCAByMZOkwsbPA2h9rnamhyphenhyphenEFWQNJC3omRiGujGdDP2CrZUC2QGdV9Csh4uc-1A4x4wUrIlu1m4UIz_cV6G0bHLleYS09bfmTv2HTLmVsKk/s1600/1-19-2015+7-56-55+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6bXTrkYEmV_724EGNhVYTi3C6VvEGSmz4NQCAByMZOkwsbPA2h9rnamhyphenhyphenEFWQNJC3omRiGujGdDP2CrZUC2QGdV9Csh4uc-1A4x4wUrIlu1m4UIz_cV6G0bHLleYS09bfmTv2HTLmVsKk/s1600/1-19-2015+7-56-55+PM.png" height="280" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
<pre class="brush : sql">SELECT trim(regexp_substr('String1,String2,String3', '[^,]+', 1, LEVEL)) str_2_tab
FROM dual
CONNECT BY LEVEL <= regexp_count('String1,String2,String3', ',')+1
</pre>
thank you : <a href="http://stackoverflow.com/questions/14328621/oracle-10g-splitting-string-into-multiple-rows">http://stackoverflow.com/questions/14328621/oracle-10g-splitting-string-into-multiple-rows</a>jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-86404113791258814812015-01-09T02:00:00.001-08:002015-01-12T02:38:42.460-08:00แก้ปัญหา Oracle SQL LISTAGG not supportLISTAGG เป็น aggregate function ที่เอาไว้ join multiple row ให้เป้น single row เช่น<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicJsIvu0xBJuI4Qnq0BkzkElA9WESnnWIhm0yyOR8j1PMS9YOShVGtFsvleZ0mnc0V4SfrS_1lgJa8xaZuuyyKF0k1ffzh101nOuySJll-vTpQqCtljYlxlgugwyri4WBIV6EHbIcL0iEG/s1600/1-9-2015+4-58-53+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicJsIvu0xBJuI4Qnq0BkzkElA9WESnnWIhm0yyOR8j1PMS9YOShVGtFsvleZ0mnc0V4SfrS_1lgJa8xaZuuyyKF0k1ffzh101nOuySJll-vTpQqCtljYlxlgugwyri4WBIV6EHbIcL0iEG/s1600/1-9-2015+4-58-53+PM.png" height="266" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZxbYVRfODkODRJu9hWDi87LgQZxmpYCE_C2hADjpqmghJxAGw2keyTi7Olzqel181TOb9hFegDxMXtP7vr-_eeB3HYU-_OEKG-0wmQC6C97o5eCleEFMTUn1XpKXHGlfcgZ5gDYKKMALb/s1600/1-9-2015+4-59-28+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZxbYVRfODkODRJu9hWDi87LgQZxmpYCE_C2hADjpqmghJxAGw2keyTi7Olzqel181TOb9hFegDxMXtP7vr-_eeB3HYU-_OEKG-0wmQC6C97o5eCleEFMTUn1XpKXHGlfcgZ5gDYKKMALb/s1600/1-9-2015+4-59-28+PM.png" height="140" width="400" /></a></div>
<br />
<br />
ถ้าเรา run SQL นี้ไม่ได้ (เนื่องจาก version ไม่ support)<br />
<pre class="brush : sql">select gp, LISTAGG(name, ', ') WITHIN GROUP (ORDER BY name) name
from(
select 'A' gp, 1 name from dual
UNION
select 'A' gp, 2 name from dual
UNION
select 'A' gp, 3 name from dual
UNION
select 'A' gp, 4 name from dual
UNION
select 'B' gp, 5 name from dual
UNION
select 'B' gp, 6 name from dual
UNION
select 'B' gp, 7 name from dual
UNION
select 'B' gp, 8 name from dual
)
group by gp
</pre>
ลอง run แบบนี้ดูครับ
<br />
<pre class="brush : sql">select gp, RTRIM (XMLAGG (XMLELEMENT (e, name || ', ')).EXTRACT ('//text()'), ', ') name
from(
select 'A' gp, 1 name from dual
UNION
select 'A' gp, 2 name from dual
UNION
select 'A' gp, 3 name from dual
UNION
select 'A' gp, 4 name from dual
UNION
select 'B' gp, 5 name from dual
UNION
select 'B' gp, 6 name from dual
UNION
select 'B' gp, 7 name from dual
UNION
select 'B' gp, 8 name from dual
)
group by gp
</pre>
ถ้าทำแล้วมันมี string ที่ซ้ำกันแล้วเราต้องการลบตัวที่ซ้ำ ใช้ ตัวนี้ช่วยครับ
<br />
<pre class="brush : sql">...
... /* replace YOUR_STRING */
...
REGEXP_REPLACE(REGEXP_REPLACE(regexp_replace( YOUR_STRING ,'([^,]+)(, \1)+', '\1'),',{2,}',','),'^,|,$','')
...
...
</pre>
Thank you : <a href="http://stackoverflow.com/questions/468990/how-can-i-combine-multiple-rows-into-a-comma-delimited-list-in-oracle">http://stackoverflow.com/questions/468990/how-can-i-combine-multiple-rows-into-a-comma-delimited-list-in-oracle</a>jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-88261914863230231952015-01-06T01:25:00.005-08:002015-01-06T04:12:04.891-08:00Excel Object Mapping บางครั้งเราอาจเจอโปรเจ็คที่ลูกค้าต้องการ ให้ import ข้อมูลจาก excel เข้าไปในฐานข้อมูล<br />
แต่มันไม่ได้ง่ายขนาดที่ว่า เอา excel มายัดลงข้อมูลได้เลย เนื่องจากรูปแบบข้อมูลที่แต่ละคนกำหนดมา ไม่เหมือนกัน (วาง column ไม่ตรง pattern ข้อมูลไม่ถูกต้อง) และต้องการให้เรา validate ข้อมูลก่อนนำเข้า จึงเป็นที่มาของการเขียนโปรแกรม เพื่ออ่านข้อมูลจาก excel file ไปเป็น java object <br />
<br />
ทำครั้งแรกครั้งที่ 2 ก็ไม่เท่าไหร่ แต่ทำซ้ำไปซ้ำมา รู้สึกว่า มันเริ่มไม่โอเคล่ะ เขียน code เดิมๆ ไม่ได้ความรู้อะไรใหม่ขึ้นมาเลย เริ่มจะไม่อยากยุ่งกับมันล่ะ ก็เลยคิดว่าทำไมเราไม่เขียนเป็น library ไว้ใช้เลยล่ะ โปรเจ็คหน้า ก็เอาตัวนี้ไปใช้ได้เลย ง่ายๆ สะดวกดี<br />
<br />
ผมจึงตัดสินใจ เขียน lib Excel Object Mapping สำหรับ Map Excel file ไปเป็น Java Object ให้อัตโนมัติครับ โดยเราแค่ตั้งชื่อ excel แถวแรก ให้ตรงตามชื่อ column ใน java class ที่เราต้องการให้ map ไป แค่นี้มันก็ map ให้แล้วครับ<br />
<br />
<a href="https://github.com/jittagornp/excel-object-mapping">https://github.com/jittagornp/excel-object-mapping</a><br />
<br />
อนาคต ผมอาจเพิ่มการ validate ข้อมูลที่ซับซ้อนขึ้นไปอีก เพื่อให้ตอบโจทย์กับสิ่งที่เรากำลังทำอยู่ :)<br />
<br />jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-68230187987011551582014-11-24T04:46:00.000-08:002014-11-24T22:52:05.903-08:00Primefaces SelectOneRadio change disabled<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN3VgcQfwNGGezOhF80Pcf0fQBqgnVEKAH5raIZPEiH8VU4ZG5O6SPOwVjUk4jW5TKagcTAp7PM604XlHeWUqfgMFlPi1JizsirQipyVs1bX4DLRqi_V2s8sUq1y5xzuixaXAV9cJpHRON/s1600/11-24-2014+7-55-03+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN3VgcQfwNGGezOhF80Pcf0fQBqgnVEKAH5raIZPEiH8VU4ZG5O6SPOwVjUk4jW5TKagcTAp7PM604XlHeWUqfgMFlPi1JizsirQipyVs1bX4DLRqi_V2s8sUq1y5xzuixaXAV9cJpHRON/s1600/11-24-2014+7-55-03+PM.png" height="98" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPlTMztTdnZWLOcut7Z_8LXggpM-mqZQ4qnWlR1cycA5y7PLzsdEa0A92VAevzhq3IfJ1jGkh56fChMaqP3cSX5_0IV9sjMlf4tXgJ6D223r-TNbzM37a57KjP-wScwoPajua6o0ChqQPM/s1600/11-24-2014+7-54-50+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPlTMztTdnZWLOcut7Z_8LXggpM-mqZQ4qnWlR1cycA5y7PLzsdEa0A92VAevzhq3IfJ1jGkh56fChMaqP3cSX5_0IV9sjMlf4tXgJ6D223r-TNbzM37a57KjP-wScwoPajua6o0ChqQPM/s1600/11-24-2014+7-54-50+PM.png" height="102" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
<br />
<a name='more'></a>js
<br />
<pre class="brush : js">/**
* @author redcrow
* create 24/11/2014
* link http://na5cent.blogspot.com/2014/11/primefaces-selectoneradio-change.html
*/
(function($) {
$(function() {
$('[class*=change-disabled]').each(function() {
var $redioChecked = $(this);
var classes = $redioChecked.attr('class').split(' ');
for (var i = 0; i < classes.length; i++) {
if (/^(change\-disabled)/.test(classes[i])) {
done(
$redioChecked.find('input[type=radio]'),
replace(classes[i], /.*\:(.*?)\(.*/),
replace(classes[i], /.*\((.*?)\).*/)
);
}
}
});
function replace(str, regEx){
return str.replace(regEx, function() {
return arguments[1];
});
}
function disabledChecked(checked, $el) {
if (checked) {
$el.addClass('disabled-item');
} else {
$el.removeClass('disabled-item');
}
}
function isDisabled($radioValue, disableValue) {
var values = disableValue.split(',');
for (var i = 0; i < values.length; i++) {
if ($.trim($radioValue) === $.trim(values[i])) {
return true;
}
}
return false;
}
function done($oneRadio, disableValue, classes) {
var clazz = classes.split(',');
for (var i = 0; i < clazz.length; i++) {
var $el = $($.trim(clazz[i]));
disabledChecked(isDisabled(
$oneRadio.filter(':checked').val(),
disableValue
), $el
);
$oneRadio.on('change', function() {
disabledChecked(isDisabled(
$(this).val(),
disableValue
), $el
);
});
}
}
});
})(jQuery);
</pre>
css
<br />
<pre class="brush : css">.disabled-item{
position : relative;
opacity: .30;
filter: alpha(opacity=30);
}
.disabled-item::after {
content: '';
display: inline-block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: all;
}
</pre>
<br />
<span style="font-size: x-large;"><b>ตัวอย่างการใช้งาน</b></span><br />
<br />
xhtml<br />
<i>กำหนด style class เป็น <span style="color: #3d85c6;"> change-disabled:true(.clinic-a)</span></i> : disabled target element (.clinic-a) เมื่อค่าตรงตามที่ตั้งไว้ (true) สามารถใส่เป็น multiple value ได้<br />
<pre class="brush : xml"><span>
<p:selectOneRadio value="#{auditFormCtrl.auditForm.audit.clinicA}"
styleClass="change-disabled:true(.clinic-a)">
<f:selectItem itemValue="true" itemLabel="Yes"/>
<f:selectItem itemValue="false" itemLabel="No"/>
</p:selectOneRadio>
</span>
<span class="clinic-a">
วันเดือนปีที่ผู้ป่วยเริ่มมีไข้
<p:spacer width="10"/>
<p:inputText value="#{auditFormCtrl.auditForm.audit.clinicADate}"
styleClass="ic-calendar">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</p:inputText>
</span>
</pre>
jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-24206700318101804652014-11-22T11:20:00.000-08:002014-11-22T19:03:14.089-08:00แก้ปัญหา time zone ด้วยการ set JVM Time Zone : java<span style="font-size: x-large;"><b>ปัญหา</b></span><br />
<br />
เนื่องจากผมทำการพัฒนา java web application และได้ทำการเช่า cloud server (VPS : Virtual Private Server) อยู่ที่ต่างประเทศ ปัญหาที่เจอคือเรื่องของเวลา ที่ไม่ตรงกัน ระหว่าง client (browser) และ server ทำให้การบันทึกข้อมูลลง database มีความผิดเพี้ยนไปจากความเป็นจริง<br />
<br />
<span style="font-size: x-large;"><b>วิธีแก้</b></span><br />
<br />
ผมลอง search หาข้อมูลเพื่อแก้ไขปัญหาดังกล่าว มีคนนึงแนะนำว่า ลอง set time zone ให้ JVM ดู น่าจะแก้ได้ ผมเลยลองทำตาม ปรากฎว่าได้จริงๆ ครับ ซึ่ง set ค่าให้ JVM ดังนี้<br />
<a name='more'></a><br />
<div style="text-align: center;">
<i><span style="color: #3d85c6;">-Duser.timezone=Asia/Bangkok</span></i></div>
<br />
พอดีผมใช้ Glassfish Web Application Server การ set ค่าเลยค่อนข้างง่าย เพราะมัน set ที่ GUI ได้เลย<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzjVREwbdNfydsU1vPlkSVNk3QS7egOJSl6wMgylpLD-jblE9CzDeThEMFiCQ_eF41mBb8p0QFX4yzbCeL4feh8LH6Osl_iuhCCiItiECklaJTqiUvltdFjl8SGoZaGdas0AQ4bFSV58dH/s1600/11-23-2014+2-14-31+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzjVREwbdNfydsU1vPlkSVNk3QS7egOJSl6wMgylpLD-jblE9CzDeThEMFiCQ_eF41mBb8p0QFX4yzbCeL4feh8LH6Osl_iuhCCiItiECklaJTqiUvltdFjl8SGoZaGdas0AQ4bFSV58dH/s1600/11-23-2014+2-14-31+AM.png" height="532" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
<b><span style="font-size: x-large;">คำถาม</span></b></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
แล้วเราจะรู้ได้ยังไง ว่าเราจะเอาค่า time zone id มาจากไหน<br />
<br />
<b><span style="font-size: x-large;">คำตอบ</span></b><br />
<br />
วิธีการของผมคือนี่เลยครับ</div>
<pre class="brush : java">for(String tz : TimeZone.getAvailableIDs()){
LOG.debug("time zone --> {}", tz);
}
</pre>
แค่นี้เราก็ได้ค่า time zone id ทั้งหมดมาแล้วครับ ซึ่งก็ขึ้นอยู่กับว่า เราจะ set time zone เป็นของอะไร<br />
ในที่นี้ผมเลือกเป็น <i>Asia/Bangkok</i><br />
<br />
thank you : <a href="http://stackoverflow.com/questions/2493749/how-to-set-a-jvm-timezone-properly">http://stackoverflow.com/questions/2493749/how-to-set-a-jvm-timezone-properly</a>jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com1tag:blogger.com,1999:blog-3030476649588243638.post-47098179233193977472014-11-21T01:18:00.002-08:002014-11-21T08:29:27.804-08:00BottomScrollFixer : javascript<div style="text-align: center;">
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVAEUCwrCNhljaLllymhzBtvoO78mMuBHKB64RNiPpHzpcq4ye8tVxRTBtYIbl8nCxfuk2OV_f40btfWG6jn6PKWlaD34unkeQQxpaJ9Bpcj4KghFGty7kawtNal9XPOfj82UHPYEzIYQv/s1600/11-19-2014+5-21-10+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVAEUCwrCNhljaLllymhzBtvoO78mMuBHKB64RNiPpHzpcq4ye8tVxRTBtYIbl8nCxfuk2OV_f40btfWG6jn6PKWlaD34unkeQQxpaJ9Bpcj4KghFGty7kawtNal9XPOfj82UHPYEzIYQv/s1600/11-19-2014+5-21-10+PM.png" height="640" width="400" /></a></div>
<br /></div>
บางที element ของเรา อาจจะยาวมากๆ จนล้นหน้าจอ (window) แล้ว element นั้น ก็เกิด bottom overflow (<i>overflow-x</i> เป็น <i>scroll</i> หรือ <i>auto</i>) ด้วย<br />
<br />
<b><span style="font-size: x-large;">ปัญหา</span></b><br />
<br />
ปัญหาที่เจอก็คือ เราต้องเลื่อน scroll ของ window ลงมาข้างล่างสุดของ element นั้นก่อน เราถึงจะสามารถเลื่อน scroll X ของ element นั้นได้ (เพราะ element มันล้นจอ)<br />
<br />
<span style="font-size: x-large;"><b>วิธีแก้</b></span><br />
<br />
ผมเลยทำการเขียน javascript ขึ้นมาเพื่อแก้ปัญหานี้ โดยให้มันทำการคำนวนและ fixed bottom scroll ให้โดยอัตโนมัติ โดยที่เราไม่ต้องเลื่อน window scroll ลงไปข้างล่าง มันจะ fixed ให้ทันทีที่เราเลื่อน scroll ไปถึง element นั้นตามขนาดที่เราได้ fixed ไว้ (<i>withFixedHeight</i>) โดยไม่จำเป็นต้องเลือนลงไปยังข้างล่างสุดของ element bottom scroll จะเลื่อนตามเราไปเรื่อยๆ จนกว่าจะพ้น element นั้นๆ<br />
<a name='more'></a><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyeqNlua3da6B9BssmJ8_1r1-6DC-1EPWQ4Epx6YBjPQ0bOEht4d2Sp6x-fzvMrlf2dnrF9KTw67cnqSqzqG1NyQEYpxYEqKKntLFWM7-9oEn29oBhYTgReknJ0RBnlUkw7lMediyKp64t/s1600/11-21-2014+4-21-43+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyeqNlua3da6B9BssmJ8_1r1-6DC-1EPWQ4Epx6YBjPQ0bOEht4d2Sp6x-fzvMrlf2dnrF9KTw67cnqSqzqG1NyQEYpxYEqKKntLFWM7-9oEn29oBhYTgReknJ0RBnlUkw7lMediyKp64t/s1600/11-21-2014+4-21-43+PM.png" height="398" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
BottomScrollFixer.js
<br />
<pre class="brush : js">/**
* @author redcrow
* create 21/11/2014
* link http://na5cent.blogspot.com/2014/11/bottomscrollfixer.html
*
* require jQuery
*/
window.BottomScrollFixer = window.BottomScrollFixer || (function($, win) {
var $win = $(win);
var Fixer = function(selector) {
this.$content = $(selector);
this.contentHeight = this.$content.height();
this.error = 0;
this.fixedHeight = 200;
this.overflowY = this.$content.css('overflow-y');
};
Fixer.prototype.withError = function(err) {
this.error = err;
return this;
};
Fixer.prototype.withFixedHeight = function(height) {
this.fixedHeight = height;
return this;
};
Fixer.prototype.listen = function() {
var top = this.$content.offset().top;
var bottom = this.contentHeight + top;
var scrollTop = $win.scrollTop();
var contentTop = $win.height() - this.error;
var scroll = scrollTop + contentTop;
var fixed = ((scroll - this.fixedHeight) > top) && (scroll < bottom);
this.$content.css(fixed ? {
'height': scroll - top,
'overflow-y': 'hidden'
} : {
'height': 'auto',
'overflow-y': this.overflowY
});
return this;
};
Fixer.fixedElement = function(selector) {
return new Fixer(selector);
};
return Fixer;
})(jQuery, window);
</pre>
<b><span style="font-size: x-large;">ตัวอย่าง
</span></b><br />
html
<br />
<pre class="brush : xml"><div id="myEelementFixer">
<div>
Element นี้ สูง 2,000px และกว้าง 2,000px <br />
เมื่อ เลื่อน scroll ไปเท่ากับ 200px (withFixedHeight(200))<br />
มันจะเกิด auto fixed bottom scroll ให้<br />
ในกรณีที่มันไม่เกิด auto fixed scroll ลองค่อยๆ เพิ่มค่า withError(error) ดูครับ
</div>
</div>
js
<style>
#myEelementFixer {
overflow: auto;
}
#myEelementFixer > div {
font-size : 12pt;
font-weight : bold;
padding: 20px;
background-color: #fafafa;
height: 2000px;
width: 2000px;
}
</style>
</pre>
<pre class="brush : js">(function($, win, BottomScrollFixer) {
$(function() {
var $win = $(win);
var fixer = BottomScrollFixer
.fixedElement('#myEelementFixer')
.withFixedHeight(200)
.listen();
$win.scroll(function() {
fixer.listen();
}).resize(function() {
fixer.listen();
});
});
})(jQuery, window, BottomScrollFixer);
</pre>
(สังเกต bottom scroll จะเลื่อนตาม)<br />
<br />
<div id="myEelementFixer">
<div>
Element นี้ สูง 2,000px และกว้าง 2,000px <br />
เมื่อ เลื่อน scroll ไปเท่ากับ 200px (withFixedHeight(200))<br />
มันจะเกิด auto fixed bottom scroll ให้<br />
ในกรณีที่มันไม่เกิด auto fixed scroll ลองค่อยๆ เพิ่มค่า withError(error) ดูครับ
</div>
</div>
<style>
#myEelementFixer {
overflow: auto;
}
#myEelementFixer > div {
font-size : 12pt;
font-weight : bold;
padding: 20px;
background-color: #fafafa;
height: 2000px;
width: 2000px;
}
body{
overflow-x : hidden;
}
</style>
<script>
/**
* @author jittagorn pitakmetagoon
* create 21/11/2014
*/
window.BottomScrollFixer = window.BottomScrollFixer || (function($, win) {
var Fixer = function(selector) {
this.$content = $(selector);
this.contentHeight = this.$content.height();
this.error = 0;
this.fixedHeight = 200;
this.overflowY = this.$content.css('overflow-y');
};
Fixer.prototype.withError = function(err) {
this.error = err;
return this;
};
Fixer.prototype.withFixedHeight = function(height) {
this.fixedHeight = height;
return this;
};
Fixer.prototype.listen = function() {
var $win = $(win);
var top = this.$content.offset().top;
var bottom = this.contentHeight + top;
var scrollTop = $win.scrollTop();
var contentHeight = $win.height() - this.error;
var scroll = scrollTop + contentHeight;
if (((scroll - this.fixedHeight) > top) && (scroll < bottom)) {
this.$content.css({
'height': scroll - top,
'overflow-y': 'hidden'
});
} else {
this.$content.css({
'height': 'auto',
'overflow-y': this.overflowY
});
}
};
Fixer.fixedElement = function(selector) {
return new Fixer(selector);
};
return Fixer;
})(jQuery, window);
</script>
<script>
(function($, win, BottomScrollFixer) {
$(function() {
var $win = $(win);
var fixer = BottomScrollFixer
.fixedElement('#myEelementFixer')
.withFixedHeight(200);
fixer.listen();
$win.scroll(function() {
fixer.listen();
}).resize(function() {
fixer.listen();
});
});
})(jQuery, window, BottomScrollFixer);
</script>jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-2880286841894950512014-11-12T22:41:00.002-08:002014-11-17T17:33:27.676-08:00age computation oracle sql code
<br />
<pre class="brush : sql">select trunc(months_between(sysdate,dob)/12) year,
trunc(mod(months_between(sysdate,dob),12)) month,
trunc(sysdate-add_months(dob,trunc(months_between(sysdate,dob)/12)*12+trunc(mod(months_between(sysdate,dob),12)))) day
from (
Select to_date('15122000','DDMMYYYY') dob
from dual
);
</pre>
output<br />
year month day<br />
13 10 29
jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-35083964372025604202014-10-22T10:15:00.003-07:002015-01-12T03:49:13.821-08:00เขียน Service Glassfish Server บน Linux Ubuntu server นี้ผมได้ทำการ set GLASSFISH_HOME ไว้ใน $PATH ของ file ~/.bashrc แล้วน่ะครับ จึงทำให้สามารถเรียก asadmin ได้เลย โดยไม่ต้องใช้ path เต็มครับ<br />
<br />
1. ไปที่ /etc/init.d/ ด้วยคำสั่ง
<br />
<pre class="brush : shell">$ cd /etc/init.d/
</pre>
2. สร้าง file service glassfish ด้วยคำสั่ง
<br />
<pre class="brush : shell">$ sudo vi glassfish
</pre>
3. เขียนชุดคำสั่ง<br />
<pre class="brush : shell">#!/bin/sh
#
# define service function
start(){
asadmin start-domain >/dev/null
}
stop(){
asadmin stop-domain >/dev/null
}
restart(){
asadmin restart-domain >/dev/null
}
#detect service argument
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo $"Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0
</pre>
4. สั่งงาน
<br />
<pre class="brush : shell">$ service glassfish start
</pre>
หรือ
<br />
<pre class="brush : shell">$ /etc/init.d/glassfish start
</pre>
jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-71324861967840539812014-10-22T02:10:00.002-07:002014-10-22T02:11:54.325-07:00Mysql Connection utf8ให้เพิ่ม <i><span style="color: #3d85c6;">useUnicode=true&characterEncoding=UTF-8</span></i> ต่อท้าย connection<br />
<pre class="brush : java">URL= jdbc:mysql://127.0.0.1:3306/mydatabase?useUnicode=true&characterEncoding=UTF-8
</pre>
jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-58264256709976473992014-10-21T22:01:00.001-07:002014-10-21T22:01:05.065-07:00concept การเขียนและการวาง architecture java web application ของผมกำลังเขียน...jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-11605545723120532942014-09-29T09:27:00.002-07:002020-01-17T09:22:19.069-08:00WeakHashMap ใน javaย้ายไปไว้ที่ <a href="https://www.jittagornp.me/blog/java-weakhashmap/">https://www.jittagornp.me/blog/java-weakhashmap/</a>jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-75921042373665673842014-09-28T10:11:00.003-07:002014-09-30T23:07:13.812-07:00jsf controller view scope ใน spring component : java<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf9EysrsZTK-RNe8KDRrygmBP_IwVz7dqpDvTFFNVxIy95K5rMf1-I6NQun7pRxYAyzXKgbafrnhyMuSC_3kLP70bqE5VoEz7St8AdzG1B9wZbZZl9JzylV94B1lkGEAMriduSGG9Z9nnI/s1600/view_through_scope_I.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf9EysrsZTK-RNe8KDRrygmBP_IwVz7dqpDvTFFNVxIy95K5rMf1-I6NQun7pRxYAyzXKgbafrnhyMuSC_3kLP70bqE5VoEz7St8AdzG1B9wZbZZl9JzylV94B1lkGEAMriduSGG9Z9nnI/s1600/view_through_scope_I.jpg" height="300" width="400" /></a></div>
<div style="text-align: center;">
<b><span style="font-size: x-large;"><br /></span></b></div>
<b><span style="font-size: x-large;">เท้าความ</span></b><br />
<br />
ในการเขียน Managed bean หรือ Controller ของ JSF (JavaServer Faces) นั้น จะต้องใช้ annotation @ManagedBean และ annotation Scoped ของ faces อย่างใดอย่างหนึ่งกำหนดไว้เสมอ ได้แก่<br />
<br />
<ul>
<li><span style="color: #3d85c6;">@ApplicationScoped</span></li>
<li><span style="color: #3d85c6;">@SessionScoped</span></li>
<li><span style="color: #3d85c6;">@ViewScoped</span></li>
<li><span style="color: #3d85c6;">@RequestScoped</span></li>
</ul>
<div>
ซึ่งเป็นตัวที่บ่งบอกวงจรชีวิตของ managed bean นั้นๆ ว่าจะมีชีวิตอยู่ได้นานเท่าใด</div>
<div>
<br /></div>
<div>
ต่อมา ผมต้องการเปลี่ยน managed bean ของ jsf ทั้งหมดที่ใช้ @ManagedBean ไปใช้ @Component ของ SpringFramework จากที่แต่ก่อน instance ของ managed bean ถูกสร้างขึ้นโดย jsf engine ก็ delegate ไปให้เป็นหน้าที่ของ spring เป็นคนสร้างให้แทน เนื่องจากต้องการให้สามารถ inject service และ component ต่างๆ ได้ง่ายขึ้น เพราะเป็น spring ด้วยกัน </div>
<div>
<br /></div>
<div>
<b><span style="font-size: x-large;">ปัญหา</span></b></div>
<div>
<br /></div>
<div>
ปัญหาที่ผมเจอคือ การสร้าง controller ที่เป็น jsf ของ spring จะต้องควบคู่กับ annotation @Scope ซึ่งมี scope ไม่ครบตาม scope เดิมของ jsf คือยังขาด scope ที่เป็น view แล้วเราจะทำยังไงล่ะ เพราะส่วนใหญ่แล้วเราต้องการ scope ที่เป็น view ซ่ะด้วย?</div>
<div>
<br /></div>
<div>
<u>คำตอบ</u><br />
<u><br /></u>
ก็สร้างมันขึ้นมาซ่ะสิ<br />
<a name='more'></a></div>
<div>
<br /></div>
<div>
<span style="font-size: x-large;"><b>วิธีการ</b></span></div>
<div>
<br /></div>
<div>
1. สร้าง scope ที่เป็น view ของ jsf ให้ @Scope<br />
ในที่นี้ เราจะ custom view scope ขึ้นมาเอง โดยผนวกมันเข้ากับ FacesContext<br />
<br /></div>
<pre class="brush : java">package com.blogspot.na5cent.spring;
import java.util.Map;
import javax.faces.context.FacesContext;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
/**
* @author redcrow
*/
public class ViewScope implements Scope {
private Map<String, Object> getViewMap() {
return FacesContext.getCurrentInstance()
.getViewRoot()
.getViewMap();
}
@Override
public Object get(String name, ObjectFactory objectFactory) {
Map<String, Object> viewMap = getViewMap();
if (viewMap.containsKey(name)) {
return viewMap.get(name);
} else {
Object object = objectFactory.getObject();
viewMap.put(name, object);
return object;
}
}
@Override
public Object remove(String name) {
return getViewMap().remove(name);
}
@Override
public String getConversationId() {
return null;
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
//Not supported
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
}
</pre>
2. ที่ file applicationContext.xml (ของ spring framework) ให้ add scope เข้าไปใน component
<br />
<pre class="brush : xml">...
...
...
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="view">
<bean class="com.blogspot.na5cent.spring.ViewScope"/> <!-- ***** -->
</entry>
</map>
</property>
</bean>
...
...
...
</pre>
3. ใช้งาน
<br />
<pre class="brush : java">...
...
...
@Component
@Scope("view") //*****
public class TopicIncomeCtrl {
private static final Logger LOG = LoggerFactory.getLogger(TopicIncomeCtrl.class);
@Autowired
private TopicIncomeService service;
private TopicIncomeLazy lazy;
private TopicIncome topic;
@PostConstruct
public void postContruct() {
lazy = new TopicIncomeLazy();
}
public TopicIncomeLazy getLazy() {
return lazy;
}
public void onCreateTopic() {
topic = new TopicIncome();
}
public TopicIncome getTopic() {
return topic;
}
public void setTopic(TopicIncome topic) {
this.topic = topic;
}
public void onAddTopic(){
service.saveTopic(topic);
}
}
</pre>
ก่อนที่จะทำตรงนี้ได้ แนะนำให้อ่าน 2 บทความนี้ก่อนครับ<br />
<a href="http://na5cent.blogspot.com/2013/07/jsf-managedbean-can-autowire-spring.html">http://na5cent.blogspot.com/2013/07/jsf-managedbean-can-autowire-spring.html</a> <a href="http://na5cent.blogspot.com/2013/09/spring-session-scope.html">http://na5cent.blogspot.com/2013/09/spring-session-scope.html</a><br />
<br />
ที่มาของภาพ <a href="http://www.coyotecanada.ca/view_through_scope_I.jpg">http://www.coyotecanada.ca/view_through_scope_I.jpg</a>jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com0tag:blogger.com,1999:blog-3030476649588243638.post-70723573131245605212014-09-23T03:39:00.000-07:002014-09-23T20:31:31.993-07:00wrap http servlet response ด้วย HttpServletResponseWrapper<b><span style="font-size: x-large;">ปัญหา</span></b><br />
<br />
อันนี้เป็นปัญหาที่ผมเจอระหว่างการพัฒนาโปรเจ็คที่ใช้ spring mvc ครับ ซึ่ง view ผมเป็น jsp<br />
ความต้องการของผมคือ ผมต้องการ parse jsp ไปเป็น html เอง (manual parse) เพราะผมต้องการ return ผลลัพธ์กลับไปเป็น json ซึ่งห่อหุ้ม html (jsp) อีกที ก็เลยได้ลองหาข้อมูลใน google อยู่สักพัก ปรากฏว่ามันไม่น่าจะทำได้ <br />
<br />
ทุกปัญหาย่อมมีทางออก!<br />
มันก็มีทางออกอยู่ว่า ถ้าอยาก parse template เอง ก็ต้องเปลี่ยนไปเขียน code ที่เป็นพวก template engine แทน jsp เช่น themeleaf, freemarker หรือ velocity อันนี้ได้ชัวร์ 100% แต่ผมไม่อยากเปลี่ยนนี่สิ เพราะผมต้องการ feature บางอย่างของ jsp ที่มีมากกว่า template engine พวกนี้ <br />
จนในที่สุด ผมก็พบทางออก ลืมไปเลย ว่ามันมีวิธีนี้อยู่ manual parse ไม่ได้ไม่เป็นไร งั้นห่อหุ่ม (wrap) ผลลัพธ์แทนแล้วกัน ซึ่งเป็นที่มาของบทความนี้ครับ<br />
<br />
<span style="font-size: x-large;"><b>วิธีการ</b></span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPY3IsLN38eB0UhYRDS_Fqf_fzLUvRGiSdr8lxYT08wUmaivZH9ASPowW_KMNmuC_mipu77SHgNX6tOWQhHqXDuWfRsW5UdHpC4sGhViz4iGGf1xOyiYTivwOE7oS2rCoMuwJmUwpCw8_R/s1600/9-23-2014+5-53-11+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPY3IsLN38eB0UhYRDS_Fqf_fzLUvRGiSdr8lxYT08wUmaivZH9ASPowW_KMNmuC_mipu77SHgNX6tOWQhHqXDuWfRsW5UdHpC4sGhViz4iGGf1xOyiYTivwOE7oS2rCoMuwJmUwpCw8_R/s1600/9-23-2014+5-53-11+PM.png" height="230" style="border: none;" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
<a name='more'></a> ผมแก้ปัญหาการ manual parse ไปเป็นการห่อหุ้ม (wrap) ผลลัพธ์ที่ถูก parse แล้วแทน สรุปก็ยังใช้ jsp เหมือนเดิม แล้วให้ servlet engine parse jsp ให้จนได้ผลลัพธ์ที่เป็น html<br />
เราค่อยเอา html นั้นมา wrap ด้วย json ตามที่เราต้องการครับ<br />
<br />
<b><span style="font-size: large;">คำถาม</span></b><br />
แล้วเราจะ wrap มันยังไง?<br />
<br />
<b><span style="font-size: large;">คำตอบ</span></b><br />
wrap ด้วย HttpServletResponseWrapper ผ่าน filter นั่นเอง<br />
<br />
มาเขียนกันเลยดีกว่าครับ ซึ่งมีแค่ 2 ขั้นตอนเท่านั้น<br />
<br />
1. สร้าง class ที่ extends HttpServletResponseWrapper เพื่อ modify response ที่ถูกส่งกลับมา ให้เป็น String ด้วยการเปลี่ยน PrintWriter ของ HttpServletResponse เดิม ไปเป็น CharArrayWritter (จริงๆ แล้วก็ไม่ได้เปลี่ยนหรอก แค้่ให้ PrintWriter มันเขียนลง CharArrayWritter แทน)<br />
<br />
CharResponseWrapper.java
<br />
<pre class="brush : java">package com.blogspot.na5cent.servlet;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
/**
* @author redcrow
*/
public class CharResponseWrapper extends HttpServletResponseWrapper {
protected CharArrayWriter charWriter;
protected PrintWriter writer;
protected boolean getOutputStreamCalled;
protected boolean getWriterCalled;
public CharResponseWrapper(HttpServletResponse response) {
super(response);
charWriter = new CharArrayWriter();
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (getWriterCalled) {
throw new IllegalStateException("getWriter already called");
}
getOutputStreamCalled = true;
return super.getOutputStream();
}
@Override
public PrintWriter getWriter() throws IOException {
if (writer != null) {
return writer;
}
if (getOutputStreamCalled) {
throw new IllegalStateException("getOutputStream already called");
}
getWriterCalled = true;
writer = new PrintWriter(charWriter);
return writer;
}
@Override
public String toString() {
String s = null;
if (writer != null) {
s = charWriter.toString();
}
return s;
}
}
</pre>
2. เขียน filter เพื่อเปลี่ยนและ wrap response ด้วย CharResponseWrapper จากข้อ 1 แล้ว manual json ก่อนส่งกลับไปให้ client (browser)<br />
Jsp2JsonResponseWrapperFilter.java
<br />
<pre class="brush : java">package com.blogspot.na5cent.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.blogspot.na5cent.servlet.CharResponseWrapper;
import com.google.gson.Gson;
import java.util.HashMap;
import java.util.Map;
import java.util.Date;
/**
* @author redcrow
*/
@WebFilter(urlPatterns = "/*")
public class Jsp2JsonResponseWrapperFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
private boolean isJsp2JsonRequest(HttpServletRequest request) {
return request.getRequestURI().contains("/json/");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (!isJsp2JsonRequest(req)) {
chain.doFilter(request, response);
return;
}
ServletResponse newResponse = new CharResponseWrapper(resp); //*****
chain.doFilter(request, newResponse);
String html = newResponse.toString();
if (html != null) {
Map<String, Object> map = new HashMap<>();
map.put("payload", html);
map.put("timestamp", new Date().getTime());
response.getWriter().write(new Gson().toJson(map));
response.setContentType("application/json");
}
}
@Override
public void destroy() {
}
}
</pre>
ถ้า file .jsp เราเป็น
<br />
<pre class="brush : xml"><%@page contentType="text/html" pageEncoding="UTF-8"%>
<h1>hello world</h1>
</pre>
ผลลัพธ์จะได้เป็น json ดังนี้
<br />
<pre class="brush : js">{"timestamp":1411477104056,"payload":"\u003ch1\u003ehello world\u003c/h1\u003e"}
</pre>
แค่นี้ ผมก็แก้ปัญหาที่ผมต้องการได้แล้วครับ<br />
<br />
<span style="font-size: x-large;"><b>เหตุผล</b></span><br />
<br />
แน่นอนว่า ต้องมีคนสงสัยว่าทำไมผมต้องทำแบบนี้<br />
<br />
<u>ตอบ</u> : เพราะผมต้องการ add additional data ลงไปใน response โดยไม่ให้ส่งผลกระทบต่อ html/jsp เดิมครับ รูปแบบ json เป็นอะไรที่อ่านแล้วเข้าใจง่ายที่สุด และใช้ร่วมกับ javascript ได้ดีที่สุด ผมมองถึงอนาคตที่ต้องมี additional data ต่างๆ อีกมากมาย รวมทั้งต้องการทำให้ response ทุกอย่างที่เป็น ajax มีรูปแบบเป็น json แบบเดี่ยวกันทั้งหมด เพื่อความง่ายในการ manage ครับ <br />
<br />jittagornphttp://www.blogger.com/profile/13814364224522529707noreply@blogger.com2