tag:blogger.com,1999:blog-3240757074102968102024-03-13T20:29:37.109+07:00Liferay, Java & ProgrammingLiferay, Programming, Java, Algorithms, Portal, Cổng thông tin, Lập trình, Thuật toán, Liferay Vietnam, Liferay Việt NamChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.comBlogger75125tag:blogger.com,1999:blog-324075707410296810.post-65757044775279548862020-08-12T12:17:00.006+07:002020-08-12T12:17:55.609+07:00Sao chép đối tượng (clone) trong JavaSao chép đối tượng (cloning) là một vấn đề khá quan trọng đối với lập trình viên <b>JAVA</b>. Mới đây thôi, trong dự án của công ty, chúng tôi cũng phải đối mặt với một vấn đề và chúng tôi giải quyết nó bằng việc sử dụng phương thức <b>clone</b>. Post này tôi sẽ lược dịch các nội dung liên quan đến phương thức clone mà tôi cho là thú vị và hữu ích.<p></p>
<p style="text-align: justify;">Trước hết thì clone là gì? Rất đơn giản: clone là việc tạo ra một đối tượng mới có nội dung giống hết đối tượng được clone.</p>
<div style="text-align: center;"><img border="0" data-original-height="788" data-original-width="940" height="336" src="https://1.bp.blogspot.com/-0Q4HRi-yJqU/XzN3eFr0FXI/AAAAAAAADCY/V_6BGarNuaosZcrFg90LcrweaatxbPaQACLcBGAsYHQ/w400-h336/cloning%2Bin%2Bjava.png" width="400" /></div><a name='more'></a>
<h2 style="text-align: left;">Phương thức clone() trong Java là gì</h2>
<p style="text-align: justify;">Phương thức clone được định nghĩa trong lớp Object. Phương thực này được sử dụng để tạo ra một đối tượng giống hệt đối tượng được sao chép. Nghĩa là, nó tạo ra một đối tượng mới và sao chép toàn bộ dữ liệu của đối tượng ban đầu.</p>
<p style="text-align: justify;">Hãy xem đoạn code sau:</p>
<blockquote><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><span style="color: #569cd6;">protected</span> <span style="color: #569cd6;">native</span> <span style="color: #4ec9b0;">Object</span> <span style="color: #dcdcaa;">clone</span>() throws CloneNotSupportedException;</div></blockquote>
<h2>Sao chép (cloning) trong Java là gì?</h2>
<p style="text-align: justify;">Khi nào chúng ta nghĩ tới khái niệm Sao chép trong Java? Là khi chúng ta có một đối tượng với nhiều thuộc tính và chúng ta muốn tạo một đối tượng mới có cùng thuộc tính và giá trị. Một số người vẫn hiểu nhầm giữa sao chép đối tượng và phép gán (=). Java không cung cấp bất kỳ phép (toán tử) sao chép một đối tượng. Nếu bạn sử dụng phép gán, nghĩa là bạn sao chép một biến tham chiếu, không phải là tạo ra một đối tượng mới.</p>
<p style="text-align: justify;">Hãy xem đoạn code dưới đây</p>
<blockquote><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">Student</span></div><div>{</div><div> <span style="color: #4ec9b0;">int</span> <span style="color: #9cdcfe;">code</span>;</div><div> <span style="color: #4ec9b0;">String</span> <span style="color: #9cdcfe;">name</span>;</div><div>}</div><div><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">Data</span></div><div>{</div><div> <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #4ec9b0;">void</span> <span style="color: #dcdcaa;">main</span>(<span style="color: #4ec9b0;">String</span>[] <span style="color: #9cdcfe;">args</span>) </div><div> {</div><div> <span style="color: #4ec9b0;">Student</span> <span style="color: #9cdcfe;">object1</span> = <span style="color: #c586c0;">new</span> <span style="color: #dcdcaa;">Student</span>();</div><div> <span style="color: #9cdcfe;">object1</span>.<span style="color: #9cdcfe;">name</span> = <span style="color: #ce9178;">"Ravi"</span>;</div><div> <span style="color: #9cdcfe;">object1</span>.<span style="color: #9cdcfe;">code</span> = <span style="color: #b5cea8;">1</span>;</div><div> </div><div> <span style="color: #4ec9b0;">Student</span> <span style="color: #9cdcfe;">object2</span> = object1;</div><div> <span style="color: #6a9955;">// Changing the value in object2 </span></div><div> <span style="color: #9cdcfe;">object2</span>.<span style="color: #9cdcfe;">name</span> = <span style="color: #ce9178;">"Ram"</span>;</div><div> <span style="color: #9cdcfe;">object2</span>.<span style="color: #9cdcfe;">code</span> = <span style="color: #b5cea8;">2</span>;</div><div> </div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Printing values from Object1:"</span>);</div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Name:"</span>+<span style="color: #9cdcfe;">object1</span>.<span style="color: #9cdcfe;">name</span> + <span style="color: #ce9178;">" code:"</span>+ <span style="color: #9cdcfe;">object1</span>.<span style="color: #9cdcfe;">code</span>);</div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Printing values from Object2:"</span>);</div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Name:"</span>+<span style="color: #9cdcfe;">object2</span>.<span style="color: #9cdcfe;">name</span> + <span style="color: #ce9178;">" code:"</span>+ <span style="color: #9cdcfe;">object2</span>.<span style="color: #9cdcfe;">code</span>); </div><div> }</div><div>}</div></div></div></blockquote><p>Kết quả khi chạy:</p><blockquote><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div>Output<span style="color: #c586c0;">:</span> <span style="color: #4ec9b0;">Printing</span> values from <span style="color: #9cdcfe;">Object1</span><span style="color: #c586c0;">:</span></div><div>Name<span style="color: #c586c0;">:</span><span style="color: #4ec9b0;">Ram</span> <span style="color: #9cdcfe;">Code</span><span style="color: #c586c0;">:</span><span style="color: #b5cea8;">2</span></div><div><span style="color: #4ec9b0;">Printing</span> values from <span style="color: #9cdcfe;">Object2</span><span style="color: #c586c0;">:</span></div><div>Name<span style="color: #c586c0;">:</span><span style="color: #4ec9b0;">Ram</span> <span style="color: #9cdcfe;">Code</span><span style="color: #c586c0;">:</span><span style="color: #b5cea8;">2</span></div></div></blockquote>
<p style="text-align: justify;">Như bạn có thể thấy, cả 2 đối tượng (object1 và object2) đều có cùng giá trị. Bởi vì chúng cùng trỏ tới một đối tượng.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-L_IhiXnQru4/XzN66QSUAGI/AAAAAAAADC0/XRkwWMfxKC4ChY2RY_uPLgjtPMURD7XrwCLcBGAsYHQ/s940/cloning%2B1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="788" data-original-width="940" height="336" src="https://1.bp.blogspot.com/-L_IhiXnQru4/XzN66QSUAGI/AAAAAAAADC0/XRkwWMfxKC4ChY2RY_uPLgjtPMURD7XrwCLcBGAsYHQ/w400-h336/cloning%2B1.png" width="400" /></a></div>
<p style="text-align: justify;">Ta có thể giải quyết vấn đề trên bằng cách cài đặt phương thức clone. Nó sẽ tạo ra một đối tượng mới và cả 2 đối tượng sẽ độc lập với nhau (nghĩa là việc thay đổi các giá trị trong đối tượng này không ảnh hưởng đến đối tượng kia và ngược lại).</p>
<blockquote><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">Student</span> <span style="color: #569cd6;">implements</span> <span style="color: #4ec9b0;">Cloneable</span></div><div>{</div><div> <span style="color: #4ec9b0;">int</span> <span style="color: #9cdcfe;">code</span>;</div><div> <span style="color: #4ec9b0;">String</span> <span style="color: #9cdcfe;">name</span>;</div><div> <span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">Object</span> <span style="color: #dcdcaa;">clone</span>() <span style="color: #569cd6;">throws</span> <span style="color: #4ec9b0;">CloneNotSupportedException</span> </div><div> { </div><div> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">super</span>.<span style="color: #dcdcaa;">clone</span>(); </div><div> } </div><div>}</div><div><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">Data</span></div><div>{</div><div> <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #4ec9b0;">void</span> <span style="color: #dcdcaa;">main</span>(<span style="color: #4ec9b0;">String</span>[] <span style="color: #9cdcfe;">args</span>) <span style="color: #569cd6;">throws</span> <span style="color: #4ec9b0;">CloneNotSupportedException</span> </div><div> {</div><div> <span style="color: #4ec9b0;">Student</span> <span style="color: #9cdcfe;">object1</span> = <span style="color: #c586c0;">new</span> <span style="color: #dcdcaa;">Student</span>();</div><div> <span style="color: #9cdcfe;">object1</span>.<span style="color: #9cdcfe;">name</span> = <span style="color: #ce9178;">"Ravi"</span>;</div><div> <span style="color: #9cdcfe;">object1</span>.<span style="color: #9cdcfe;">code</span> = <span style="color: #b5cea8;">1</span>;</div><div> </div><div> <span style="color: #4ec9b0;">Student</span> <span style="color: #9cdcfe;">object2</span> = (Student) <span style="color: #9cdcfe;">object1</span>.<span style="color: #dcdcaa;">clone</span>();</div><div> </div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Printing values from Object1:"</span>);</div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Name:"</span>+<span style="color: #9cdcfe;">object1</span>.<span style="color: #9cdcfe;">name</span> + <span style="color: #ce9178;">" Code:"</span>+ <span style="color: #9cdcfe;">object1</span>.<span style="color: #9cdcfe;">code</span>);</div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Printing values from Object2:"</span>);</div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Name:"</span>+<span style="color: #9cdcfe;">object2</span>.<span style="color: #9cdcfe;">name</span> + <span style="color: #ce9178;">" Code:"</span>+ <span style="color: #9cdcfe;">object2</span>.<span style="color: #9cdcfe;">code</span>);</div><div> </div><div> <span style="color: #6a9955;">// Changing the value in object2 </span></div><div> <span style="color: #9cdcfe;">object2</span>.<span style="color: #9cdcfe;">name</span> = <span style="color: #ce9178;">"Ram"</span>;</div><div> <span style="color: #9cdcfe;">object2</span>.<span style="color: #9cdcfe;">code</span> = <span style="color: #b5cea8;">2</span>;</div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"After changes in values"</span>);</div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Printing values from Object1:"</span>);</div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Name:"</span>+<span style="color: #9cdcfe;">object1</span>.<span style="color: #9cdcfe;">name</span> + <span style="color: #ce9178;">" Code:"</span>+ <span style="color: #9cdcfe;">object1</span>.<span style="color: #9cdcfe;">code</span>);</div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Printing values from Object2:"</span>);</div><div> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Name:"</span>+<span style="color: #9cdcfe;">object2</span>.<span style="color: #9cdcfe;">name</span> + <span style="color: #ce9178;">" Code:"</span>+ <span style="color: #9cdcfe;">object2</span>.<span style="color: #9cdcfe;">code</span>);</div><div> }</div><div>}</div></div></blockquote>
<p style="text-align: justify;">Kết quả khi chạy: </p>
<blockquote><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div>Output<span style="color: #c586c0;">:</span> <span style="color: #4ec9b0;">Printing</span> values from <span style="color: #9cdcfe;">Object1</span><span style="color: #c586c0;">:</span> Name<span style="color: #c586c0;">:</span><span style="color: #4ec9b0;">Ravi</span> <span style="color: #9cdcfe;">Code</span><span style="color: #c586c0;">:</span><span style="color: #b5cea8;">1</span></div><div><span style="color: #4ec9b0;">Printing</span> values from <span style="color: #9cdcfe;">Object2</span><span style="color: #c586c0;">:</span>Name<span style="color: #c586c0;">:</span><span style="color: #4ec9b0;">Ravi</span> <span style="color: #9cdcfe;">Code</span><span style="color: #c586c0;">:</span><span style="color: #b5cea8;">1</span></div><div><span style="color: #4ec9b0;">After</span> changes in values</div><div><span style="color: #4ec9b0;">Printing</span> values from <span style="color: #9cdcfe;">Object1</span><span style="color: #c586c0;">:</span> Name<span style="color: #c586c0;">:</span><span style="color: #4ec9b0;">Ravi</span> <span style="color: #9cdcfe;">Code</span><span style="color: #c586c0;">:</span><span style="color: #b5cea8;">1</span></div><div><span style="color: #4ec9b0;">Printing</span> values from <span style="color: #9cdcfe;">Object2</span><span style="color: #c586c0;">:</span> Name<span style="color: #c586c0;">:</span><span style="color: #4ec9b0;">Ram</span> <span style="color: #9cdcfe;">Code</span><span style="color: #c586c0;">:</span><span style="color: #b5cea8;">2</span></div></div></blockquote>
<div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-yjIuzpO20u4/XzN7CZcZgCI/AAAAAAAADC4/Mph1a82isnkvf3PWekx6tR1X3noXM5TYgCLcBGAsYHQ/s940/_cloning%2B2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="788" data-original-width="940" height="336" src="https://1.bp.blogspot.com/-yjIuzpO20u4/XzN7CZcZgCI/AAAAAAAADC4/Mph1a82isnkvf3PWekx6tR1X3noXM5TYgCLcBGAsYHQ/w400-h336/_cloning%2B2.png" width="400" /></a></div><p>Phương thức clone() tạo ra đối mới và sao chép các trường dữ liệu của đối tượng cũ. Tuy nhiên, việc sao chép này chỉ áp dụng cho các trường dữ liệu nguyên thuỷ, các trường dữ liệu tham chiếu của đối tượng mới vẫn trỏ đến các vùng dữ liệu tương ứng của đối tượng cũ. Chúng ta gọi đây là sao chép nông (shallow copy).</p>
<h2 style="text-align: left;">Sử dụng phương thức clone() như thế nào?</h2>
<ol style="text-align: left;"><li>Trong Java, nếu bạn muốn sử dụng sao chép, bạn phải ghi đè phương thức clone() trong lớp mà bạn tạo ra.</li><li>Để sử dụng phương thức clone, bạn phải cài đặt giao diện Cloneable. Nếu không cài đặt giao diện, JVM sẽ ném ra ngoại lệ CloneNoSupportedException.</li><li>Khi bạn cài đặt phương thức clone(), bạn phải gọi đến phương thức clone của lớp cha. </li></ol>
<p style="text-align: justify;">Bạn cần chú ý những điều quan trọng sau đây:</p>
<ol style="text-align: left;">
<li>Đối tượng trả về từ phương thức clone() là giống hệt đối tượng cũ nhưng sẽ trỏ đến vùng nhờ khác nhau trong HEAP. Do đó, phép so sanh sau sẽ có kết quả là false<br /><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #6a9955;">// It returns false, because both are different object in heap memory</span></div><div> <span style="color: #9cdcfe;">a</span>.<span style="color: #dcdcaa;">clone</span>() == a; </div></div></li>
<li>Dữ liệu bên trong đối tượng mới được tạo ra sẽ giống hệt đối tượng cũ. Do đó, phương thức so sánh giữa 2 đối tượng sẽ trả về kết quả là true.<br /><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #6a9955;">// It returns true, because both object contains same values</span></div><div><span style="color: #9cdcfe;">x</span>.<span style="color: #dcdcaa;">clone</span>().<span style="color: #dcdcaa;">equals</span>(x);</div></div></li>
</ol>
<h2>Phương thức clone() làm việc như thế nào?</h2>
<p style="text-align: justify;">Mặc định, lớp Object cài đặt nội dung của phương thức clone().</p>
<p style="text-align: justify;">Bước 1: JVM kiểm tra xem lớp của bạn đã cài đặt giao diện Cloneable chưa? Nếu chưa, sẽ có ngoại lệ CloneNotSupportedException được ném ra.</p>
<p style="text-align: justify;">Bước 2: JVM sẽ kiểm tra các ngoại lệ khác, cái mà cần phải xử lý trong quá trình sao chép.</p>
<p style="text-align: justify;">Bước 3: Phương thức clone sẽ tạo ra một bản sao (shallow copy) và sau đó sao chép tất cả nội dung từ đối tượng cũ sang đối tượng mới được tạo.</p>
<p style="text-align: justify;">Mặc định, lớp Object cài đặt nội dung của phương thức clone().</p>
<h2 style="text-align: left;">JVM làm việc thế nào với phương thức sao chép mặc định?</h2>
<p style="text-align: justify;">JVM gọi phương thức sao chép (shallow cloning), nghĩa là nó thực hiện các bước sau:</p>
<ol style="text-align: left;">
<li>Nếu lớp chỉ chứa các dữ liệu nguyên thuỷ, thì nó sẽ sao chép hết các dữ liệu trong đối tượng cũ sang đối tượng mới và trả về một tham chiếu đến đối tượng vừa được tạo.</li>
<li>Nếu lớp chứa các dữ liệu là đối tượng của lớp khác, thì nó tạo ra đối tượng mới và một tham chiếu đến đối tượng vừa tạo. Nhưng các thành phần trong nó sẽ không được sao chép mà nó chỉ sao chép các tham chiếu của các thành phần (nghĩa là các thành phần bên trong đối tượng cũ và mới đều trỏ đến cùng một vùng bộ nhớ).</li>
</ol>
<h2>Các loại sao chép</h2>
<p style="text-align: justify;">Có 2 loại sao chép là:</p>
<ul>
<li>Sao chép nông</li>
<li>Sao chép sâu.</li>
</ul>
<p style="text-align: justify;">Mình sẽ viết thêm các bài viết về 2 chủ đề này sau.</p>
<h2>Các lợi ích của việc sao chép</h2>
<p style="text-align: justify;">Sau đây là một số lợi ích của việc so chép đối tượng</p>
<ol>
<li>Nếu muốn tạo một đối tượng mới với nội dung giống hết cái cũ thì bạn không cần phải viết lại code, nghĩa là tránh được trùng lặp code. Bạn chỉ cần cài đặt giao diện Cloneable và ghi đè phương thức clone() trong lớp của bạn.</li>
<li>Nếu bạn là người bảo trì code cũ, bạn chỉ cần copy đối tượng cũ và thực hiện các thay đổi mong muốn. Sao chép là cách đơn giản và hiệu quả nhất</li>
<li>Sao chép là cách nhất để sao chép một mảng</li>
<li>Hiệu quả hơn so với các constructor sao chép. Trong constructor sao chép, bạn phải copy tất cả dữ liệu một cách rõ ràng. Nghĩa là bạn phải gán lại tất cả dữ liệu (các trường). Nhưng với việc sử dụng phương thức clone(), bạn có thể thực hiện việc sao chép với vài dòng code.</li>
</ol>
<h2>Tạm kết</h2>
<p style="text-align: justify;">Theo tôi thì đây là chủ đề đơn giản nhưng khá thú vị. Việc sử dụng phương thức clone sẽ làm cho code của bạn trở lên dễ đọc hơn. Bạn có thể đọc bài viết gốc tại <a href="https://javagoal.com/cloning-in-java/" target="_blank">đây</a>.</p>ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-72421058465666184872020-08-03T09:00:00.002+07:002020-08-12T12:51:02.918+07:00Liferay 7: Sử dụng Item Selector<p style="text-align: justify;">Trong phiên bản 7, Liferay đã giới thiệu rất nhiều tính năng mới thú vụ. Một trong số chúng là Item Selector. Nói một cách đơn giản, nó là một thành phần (component) cho phép người dùng chọn nhiều loại tài nguyên khác nhau (ảnh, video, bài viết,...).</p>
<p style="text-align: justify;">Nếu ứng dụng (portlet) của bạn cần phải chọn ảnh/video/bài viết,... từ hệ thống thì Item Selector chính một lựa chọn hợp lý. Item Selector sẽ mở ra một cửa sổ (dialog) lựa chọn cho phép bạn có thể chọn nhiều loại tài nguyên khác nhau như ảnh, video, hoặc bất kỳ dữ liệu nào.</p>
<div style="text-align: center;"><img alt="item selector" border="0" data-original-height="788" data-original-width="940" height="336" src="https://1.bp.blogspot.com/-_loMHI3Obww/Xydl694VE9I/AAAAAAAADB0/eJ956IhzIo0TYnbFa4_bkUOvE7APPI5PwCLcBGAsYHQ/w400-h336/Item%2Bselector.png" title="item selector" width="400" /></div><a name='more'></a>
<h2 style="text-align: left;">Tại sao cần Item Selector</h2>
<p style="text-align: justify;">
Nếu không có Item Selector thì ở mỗi ứng dụng và mỗi loại dữ liệu chúng ta cần
phải tự cài đặt. Việc này thực sự tồn nhất nhiều thời gian. công sức và tất
nhiên nó sẽ không thống nhất.
</p>
<p style="text-align: justify;">
Item Selector mang đến một giải pháp đơn gian, dễ dàng mở rộng và quan trọng
nhất là tính nhất quản. Có nghĩa là với Item Selector bạn có thể dùng cho bất
cứ loại dữ liệu nào mà bạn tạo ra mà vẫn đảm bảo tính nhất quản. Về cơ bản, có
hai thứ cần dùng Item Selector: <b>Entity</b> và <b>Return Type</b>
</p>
<div>
<ol style="text-align: left;">
<li style="text-align: justify;">
<b>Entity</b>: Loại dữ liệu mà bạn muốn chọn (ảnh, video...)? Mỗi loại
entity phải cài đặt giao diện
<a href="https://docs.liferay.com/portal/7.0/javadocs/modules/apps/collaboration/item-selector/com.liferay.item.selector.api/com/liferay/item/selector/ItemSelectorCriterion.html" target="_blank">ItemSelectorCriterion</a>
để Item Selector có thể hiểu. Liferay đã thực hiện cài đặt cho những
Entity cơ bản như ảnh, video,... Bạn có thể tìm được danh sách các
Entity của Liferay tại <a href="https://help.liferay.com/hc/en-us/articles/360017892072-Introduction-to-Item-Selector-Criterion-and-Return-Types-" target="_blank">đây</a>. Nếu bạn muốn tạo ra một loại Entity mới và hiển thị trong Item
Selector thì bạn cần mở rộng từ lớp <a href="https://docs.liferay.com/portal/7.0/javadocs/modules/apps/collaboration/item-selector/com.liferay.item.selector.api/com/liferay/item/selector/BaseItemSelectorCriterion.html" target="_blank">BaseItemSelectorCriterion</a>. Lớp này cũng được cài đặt từ giao diện <a href="https://docs.liferay.com/portal/7.0/javadocs/modules/apps/collaboration/item-selector/com.liferay.item.selector.api/com/liferay/item/selector/ItemSelectorCriterion.html" target="_blank">ItemSelectorCriterion</a>.
</li>
<li style="text-align: justify;">
<b>Return Type</b>: Loại dữ liệu nào mà bạn muốn cung cấp cho người dùng
khi họ chọn một Entity trên Item Selector? Bạn muốn nhận được URL, UUID
hay khoá chính (ID)? Mỗi loại dữ liệu trả về phải được cài đặt từ giao
diện <a href="https://docs.liferay.com/portal/7.0/javadocs/modules/apps/collaboration/item-selector/com.liferay.item.selector.api/com/liferay/item/selector/ItemSelectorReturnType.html" target="_blank">ItemSelectorReturnType</a>. Liferay cũng đã cung cấp sẵn một số loại Return Type như URL, UUID và
bạn có thể xem chúng tại
<a href="https://dev.liferay.com/develop/reference/-/knowledge_base/7-0/item-selector-criterion-and-return-types" target="_blank">đây</a>.
</li>
</ol>
</div>
<p style="text-align: justify;">
Vậy, Item Selector là một tổ hợp của các Entity và Return Type. Sau đây, tôi
sẽ giải thích cách Item Selector để chọn hình ảnh và trả về URL. Các bước thực
hiện như sau:
</p>
<ol style="text-align: left;">
<li style="text-align: justify;">Tạo URL từ Entity và Return Type</li>
<li style="text-align: justify;">Sử dụng URL ở bước 1 trong cửa sổ Item Selector </li>
</ol>
<h2 style="text-align: left;">Tạo URL từ Entity và Return Type</h2>
<p style="text-align: justify;">
Đầu tiên, bạn cần phải tạo URL từ tổ hợp các Entity và Return Type. Ở đây
Entity là hình ảnh và Return Type là URL.
</p>
<p>
<b>Thêm thư viện: Thêm các module vào file build.gradle.</b>
</p>
<blockquote>
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;">
<div>dependencies { </div><div> compileOnly group: <span style="color: #ce9178;">"com.liferay.portal"</span>,
<span> </span>name: <span style="color: #ce9178;">"com.liferay.portal.kernel"</span>, version: <span style="color: #ce9178;">"2.0.0"</span>,<br /></div><div> compileOnly group: <span style="color: #ce9178;">"com.liferay.portal"</span>, <span> </span>
</div><div> <span> </span>name: <span style="color: #ce9178;">"com.liferay.util.taglib"</span>, version: <span style="color: #ce9178;">"2.0.0"</span><br /></div><div> compileOnly group: <span style="color: #ce9178;">"javax.portlet"</span>,
<span> </span>name: <span style="color: #ce9178;">"portlet-api"</span>, version: <span style="color: #ce9178;">"2.0"</span><br /></div><div> compileOnly group: <span style="color: #ce9178;">"javax.servlet"</span>,</div><div> <span> </span>name: <span style="color: #ce9178;">"javax.servlet-api"</span>, version: <span style="color: #ce9178;">"3.0.1"</span><br /></div><div> compileOnly group: <span style="color: #ce9178;">"jstl"</span>, name: <span style="color: #ce9178;">"jstl"</span>, version: <span style="color: #ce9178;">"1.2"</span><br /></div><div> compileOnly group: <span style="color: #ce9178;">"org.osgi"</span>,<br /></div><div> <span> </span>name: <span style="color: #ce9178;">"org.osgi.compendium"</span>, version: <span style="color: #ce9178;">"5.0.0"</span><br /></div><div> compileOnly group: <span style="color: #ce9178;">"com.liferay"</span>,<br /></div><div> <span> </span>name: <span style="color: #ce9178;">"com.liferay.item.selector.api"</span>, version: <span style="color: #ce9178;">"2.0.0"</span><br /></div><div> compileOnly group: <span style="color: #ce9178;">"com.liferay"</span>,</div><div> <span> </span>name: <span style="color: #ce9178;">"com.liferay.item.selector.criteria.api"</span>, version: <span style="color: #ce9178;">"2.0.0"</span><br /></div><div>}</div></div>
</blockquote>
<p><b>Tạo một OSGI Service Item Selector Component trong lớp *Portlet:</b></p>
<p dir="ltr" style="background-color: white; color: #4e4e4e; line-height: 1.656; margin: 0pt 0px; padding: 0px; text-align: justify; transition: all 0.4s ease 0s;">
<b><font face="Arial"></font></b>
</p>
<blockquote>
<div style="background-color: #1e1e1e; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #569cd6;">private</span><span> </span><span style="color: #4ec9b0;">ItemSelector</span><span> </span><span style="color: #9cdcfe;">_itemSelector</span><font color="#d4d4d4">;</font>
<span style="color: #d4d4d4;">@</span><span style="color: #4ec9b0;">Reference</span><span style="color: #d4d4d4;">(unbind = </span><span style="color: #ce9178;">"-"</span><span style="color: #d4d4d4;">)</span>
<br /></div><div><span style="color: #569cd6;">public</span><span> </span><span style="color: #4ec9b0;">void </span><span style="color: #dcdcaa;">setItemSelector</span><span style="color: #d4d4d4;">(</span><span style="color: #4ec9b0;">ItemSelector </span><span style="color: #d4d4d4;">itemSelector) {</span><br /></div><div style="color: #d4d4d4;"> _itemSelector = itemSelector;<br /></div><div style="color: #d4d4d4;">}</div></div>
</blockquote>
<p><b>Thêm Return Type</b></p>
<blockquote>
<div style="background-color: #1e1e1e; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><span style="color: #4ec9b0;">List</span><span style="color: #d4d4d4;"><</span><span style="color: #4ec9b0;">ItemSelectorReturnType</span><span style="color: #d4d4d4;">> </span><span style="color: #9cdcfe;">itemSelectorReturnTypes</span><div> <font color="#d4d4d4"> = </font><span style="color: #c586c0;">new</span><span> </span><span style="color: #4ec9b0;">ArrayList</span><font color="#d4d4d4"><>();</font>
</div><div><span style="color: #9cdcfe;">itemSelectorReturnTypes</span><font color="#d4d4d4">.</font><span style="color: #dcdcaa;">add</span><font color="#d4d4d4">(</font><span style="color: #c586c0;">new</span><span> </span><span style="color: #dcdcaa;">URLItemSelectorReturnType</span><font color="#d4d4d4">());</font><br /></div></div>
</blockquote>
<p><b>Đặt Return Type trong Image Item Selector:</b></p>
<blockquote>
<div>
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><span style="color: #9cdcfe;">imageItemSelectorCriterion</span>.<span style="color: #dcdcaa;">setDesiredItemSelectorReturnTypes</span>(<div> itemSelectorReturnTypes );</div></div>
</div>
</blockquote>
<p><b>Tạo đối tượng RequestBackedPortletURLFactory:</b></p>
<blockquote>
<div>
<div style="background-color: #1e1e1e; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #4ec9b0;">RequestBackedPortletURLFactory</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">requestBackedPortletURLFactory</span><span style="color: #d4d4d4;"> =
</span><span style="color: #9cdcfe;">RequestBackedPortletURLFactoryUtil</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">create</span><span style="color: #d4d4d4;">(request);</span></div></div>
</div>
</blockquote>
<p>RequestBackedPortletURLFactory được sử dụng để tạo ra Portlet URL</p>
<p><b>Tạo URL bằng phương thức getItemSelectorURL</b></p>
<blockquote>
<div>
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><span style="color: #4ec9b0;">PortletURL </span><span style="color: #9cdcfe;">itemSelectorURL</span> = <span style="color: #9cdcfe;">_itemSelector</span>.<div> <span style="color: #dcdcaa;">getItemSelectorURL(</span>requestBackedPortletURLFactory,
</div><div> <span style="color: #ce9178;">"sampleTestSelectItem"</span>, imageItemSelectorCriterion);</div></div>
</div>
</blockquote>
<div style="text-align: justify;">Ý nghĩa của các tham số:</div>
<ol style="text-align: left;">
<li style="text-align: justify;">RequestBackedPortletURLFactory: Là đối tượng của lớp RequestBackedPortletURLFactory và nó được sử dụng để tạo ra các Portlet URL</li>
<li style="text-align: justify;">Event Name: Là tên sự kiện duy nhất sẽ được gọi bởi Item Selector khi một mục được chọn.</li>
<li style="text-align: justify;">ItemSelectorCriterion: Là một đối tượng của lớp ItemSelectorCriterion. Ở đây là cho các hình ảnh</li>
</ol>
<h2 style="text-align: justify;">Sử dụng URL trong Item Selector</h2>
<p style="text-align: justify;">Khai báo thư viện AUI</p>
<blockquote>
<div>
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #f44747;"><</span>%@ taglib prefix="aui" uri="http://liferay.com/tld/aui" %> </div></div>
</div>
</blockquote>
<p>Tạo một button và sử dụng module liferay-item-selector-dialog:</p>
<blockquote>
<div style="background-color: #1e1e1e; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: grey;"><</span><span style="color: #569cd6;">aui:button</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">name</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"chooseImage"</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">value</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Choose"</span><span style="color: grey;">/></span><span style="color: #d4d4d4;">
<br /></span></div><div style="color: #d4d4d4;"><span style="color: #f44747;"><</span>%
</div><div style="color: #d4d4d4;"> String itemSelectorURL = GetterUtil.getString(</div><span style="color: #d4d4d4;"> </span><span style="color: #d4d4d4;">request.getAttribute("itemSelectorURL"));</span><br /><div style="color: #d4d4d4;">%></div><div><span style="color: #d4d4d4;">
</span><span style="color: grey;"><</span><span style="color: #569cd6;">aui:script</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">use</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"liferay-item-selector-dialog"</span><span style="color: grey;">></span><span style="color: #d4d4d4;"><br /></span></div><div style="color: #d4d4d4;"> $('#
<span style="color: grey;"><</span><span style="color: #569cd6;">portlet:namespace</span><span style="color: grey;">/></span>chooseImage').on('click', <br /></div><div style="color: #d4d4d4;"> function(event) {</div><div style="color: #d4d4d4;"> var itemSelectorDialog = new A.LiferayItemSelectorDialog({</div><div style="color: #d4d4d4;"> eventName: 'ItemSelectedEventName',</div><div style="color: #d4d4d4;"> on: {</div><div style="color: #d4d4d4;"> selectedItemChange: function(event) {</div><div style="color: #d4d4d4;"> var selectedItem = event.newVal; </div><div style="color: #d4d4d4;"> if (selectedItem) {</div><div style="color: #d4d4d4;"> var itemValue = JSON.parse(</div><div style="color: #d4d4d4;"> selectedItem.value</div><div style="color: #d4d4d4;"> );</div><div style="color: #d4d4d4;"> itemSrc = itemValue.url;</div><div style="color: #d4d4d4;"> <span style="color: #6a9955;"><!-- use item as needed --></span></div><div style="color: #d4d4d4;"> }</div><div style="color: #d4d4d4;"> }</div><div style="color: #d4d4d4;"> },</div><div><span style="color: #d4d4d4;"> title: '</span><span style="color: grey;"><</span><span style="color: #569cd6;">liferay-ui:message</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">key</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"select-image"</span><span style="color: grey;">/></span><span style="color: #d4d4d4;">',<br /></span></div><div style="color: #d4d4d4;"> url: '<span style="color: #f44747;"><</span>%= itemSelectorURL.toString() %>'<br /></div><div style="color: #d4d4d4;"> });</div><div style="color: #d4d4d4;"> itemSelectorDialog.open();</div><div style="color: #d4d4d4;"> }</div><div style="color: #d4d4d4;"> );</div><div style="color: #d4d4d4;"><span style="color: grey;"></</span><span style="color: #569cd6;">aui:script</span><span style="color: grey;">></span><br /></div></div>
</blockquote>
<h2 style="text-align: left;">Tạm kết</h2>
<p style="text-align: justify;">Tận dụng hết được các tính năng được cung cấp sẵn bởi Liferay sẽ giúp bạn giảm được đáng kể thời gian và công sức. Đồng thời ứng dụng của bạn cũng sẽ trở lên thống nhất trong ứng dụng và với các ứng dụng khác trong hệ thống.</p>
<p>Bạn có thể tham khảo bài viết gốc tại <a href="https://www.surekhatech.com/blog/item-selector-in-liferay-dxp-7?p_p_auth=WhHRfMv4" target="_blank">đây</a>.</p>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0Hanoi, Hoàn Kiếm, Hanoi, Vietnam21.0277644 105.8341598-7.2824694361788467 70.6779098 49.337998236178848 140.9904098tag:blogger.com,1999:blog-324075707410296810.post-19011455687945821942020-07-29T08:00:00.012+07:002020-08-14T09:17:53.579+07:00Liferay 7: Thay đổi giao diện trang quản trị<p style="text-align: justify;">Trong thực tế, chúng ta thường quan tâm đến giao diện của người dùng cuối (end-user) nhiều hơn so với giao diện quản trị. Chính vì vậy, giao diện quản trị thường không được đầu tư nhiều công sức. Tuy nhiên, tuy từng từ dự án, những người dùng được cấp tài khoản có thể truy cập được trang quản trị (nhân viên, biên tập viên,...). Trong trường hợp này, đương nhiên là chúng ta cần cải thiện giao diện quản trị mặc định để người dùng cảm thấy hứng thú hơn khi sử dụng ứng dụng. Bài viết này sẽ hướng dẫn cách cập nhật giao diện mặc định của <b>Liferay 7.x.</b></p>
<p style="text-align: center;">
<img border="0" data-original-height="315" data-original-width="600" height="210" src="https://1.bp.blogspot.com/-gAUd_ajfExE/Xx_8GWlvTBI/AAAAAAAAC-M/yqqJsa2gR2spDx0MI8HQ7npueV-mzMw8wCLcBGAsYHQ/w400-h210/Change-look-and-feel-of-control-panel--600x315.png" width="400" />
</p>
<a name='more'></a>
<h2 style="text-align: left;">Bước 1: Tạo mới theme</h2>
<p style="text-align: justify;">Bạn có thể tạo mới một module theme theo hướng dẫn ở <a href="https://help.liferay.com/hc/en-us/articles/360018166591-Creating-Themes" target="_blank">đây</a>. Sau khi tạo theme mới, ta sẽ có các file và các thư mục mặc định như sau:</p>
<p class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-NUF1DUeDxp8/Xx_945w_b6I/AAAAAAAAC-Y/X-BoVu9CtA8Q3k4bMf2eRLbHazsIuGBXQCLcBGAsYHQ/s338/Change-look-and-feel-of-control-panel-1.png" style="margin-left: 1em; margin-right: 1em;">
<img alt="Các file và thư mục mặc định của theme trên eclipse IDE" border="0" data-original-height="277" data-original-width="338" height="262" src="https://1.bp.blogspot.com/-NUF1DUeDxp8/Xx_945w_b6I/AAAAAAAAC-Y/X-BoVu9CtA8Q3k4bMf2eRLbHazsIuGBXQCLcBGAsYHQ/w320-h262/Change-look-and-feel-of-control-panel-1.png" title="Các file và thư mục mặc định của theme trên eclipse IDE" width="320" /></a></p>
<h2 style="text-align: left;">Bước 2: Cập nhật file liferay-look-and-feel.xml</h2>
<p style="text-align: justify;">File liferay-look-and-feel.xml là nơi để cấu hình các thông tin của theme. Bạn có thể đọc thêm thông tin về cấu hình theme tại <a href="https://help.liferay.com/hc/en-us/articles/360018177871-Configuring-liferay-look-and-feel-xml" target="_blank">đây</a>.</p>
<p style="text-align: justify;">Trong bài viết này, chúng ta sẽ chỉ quan tâm đến việc thay đổi giao diện quản trị. Do đó, chúng ta chỉ quan tâm đến cấu hình <i><b>control-panel-theme</b></i>.</p>
<blockquote><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: grey;"><?</span><span style="color: #569cd6;">xml</span><span style="color: #9cdcfe;"> version</span>=<span style="color: #ce9178;">"1.0"</span><span style="color: grey;">?></span></div><div><span style="color: grey;"><!</span><span style="color: #569cd6;">DOCTYPE</span> <span style="color: #569cd6;">look-and-feel</span> PUBLIC "-//Liferay//DTD Look and Feel 7.2.0//EN" "http://www.liferay.com/dtd/liferay-look-and-feel_7_2_0.dtd"<span style="color: grey;">></span></div><div> </div><div><span style="color: grey;"><</span><span style="color: #569cd6;">look-and-feel</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">compatibility</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">version</span><span style="color: grey;">></span>7.2.0+<span style="color: grey;"></</span><span style="color: #569cd6;">version</span><span style="color: grey;">></span></div><div> <span style="color: grey;"></</span><span style="color: #569cd6;">compatibility</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">theme</span> <span style="color: #9cdcfe;">id</span>=<span style="color: #ce9178;">"custom-admin"</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"custom-admin"</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">control-panel-theme</span><span style="color: grey;">></span>true<span style="color: grey;"></</span><span style="color: #569cd6;">control-panel-theme</span><span style="color: grey;">></span></div><div> <span style="color: grey;"></</span><span style="color: #569cd6;">theme</span><span style="color: grey;">></span></div><div><span style="color: grey;"></</span><span style="color: #569cd6;">look-and-feel</span><span style="color: grey;">></span></div></div></div></blockquote>
<p style="text-align: justify;">Cụ thể: chúng ta đặt giá trị của <i><b>control-panel-theme</b> </i>là <i style="font-weight: bold;">true</i>. Điều này có nghĩa là, theme <b style="font-style: italic;">customer-admin </b>là theme dành cho giao diện quản trị.</p>
<h2 style="text-align: left;">Bước 3: Ghi đè các thư mục của theme</h2>
<p style="text-align: justify;">Khi thực hiện build theme, các thư mục: css, js, images và templates sẽ được sinh ra tự động. Hoặc chúng ta có thể ghi đè bằng cách copy các thư mục tại <a href="https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-classic/src" target="_blank">đây</a> vào thư mục <i><b>{customer-admin-theme}/src.</b></i></p>
<h2 style="text-align: left;">Bước 4: Chỉnh sửa các tài nguyên (CSS, JS, Images, Template)</h2>
<p style="text-align: justify;">Sau khi sao chép các thư mục trên, chúng ta có thể chỉnh sửa/ghi đè các file mà chúng ta mong muốn. Ví dụ: thay đổi màu của trang quản trị, thay các ảnh mặc định, chỉ sửa bố cục trạng....</p>
<p style="text-align: justify;">Trong bài viết này, chúng ta sẽ làm một ví dụ đơn giản đó là thay đổi màu của trang quản trị. Việc này thực sử đơn giản, chỉ cần chỉnh sửa file <i><b>_custom.scss</b></i>.</p>
<p style="text-align: justify;">Bây giờ, bạn hãy mở thư mục CSS và mở file _custom.css và thêm nội dung như sau:</p>
<blockquote><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #d7ba7d;">.product-menu</span> <span style="color: #d7ba7d;">.panel</span> <span style="color: #d7ba7d;">.panel-body</span> {</div><div> <span style="color: #9cdcfe;">background-color</span>: <span style="color: #ce9178;">#21716a</span>;</div><div>}</div><div> </div><div><span style="color: #d7ba7d;">.product-menu</span> <span style="color: #d7ba7d;">.panel</span> <span style="color: #d7ba7d;">.list-group-heading.active</span>,</div><div><span style="color: #d7ba7d;">.product-menu</span> <span style="color: #d7ba7d;">.panel</span> <span style="color: #d7ba7d;">.list-group-heading.active:hover</span>,</div><div><span style="color: #d7ba7d;">.product-menu</span> <span style="color: #d7ba7d;">.panel</span> <span style="color: #d7ba7d;">.list-group-heading.active:focus</span> {</div><div> <span style="color: #9cdcfe;">background-color</span>: <span style="color: #ce9178;">#21716a</span>;</div><div>}</div><div> </div><div><span style="color: #d7ba7d;">.product-menu</span> <span style="color: #d7ba7d;">.panel</span> <span style="color: #d7ba7d;">.list-group-heading.active</span>+<span style="color: #d7ba7d;">div</span> {</div><div> <span style="color: #9cdcfe;">background-color</span>: <span style="color: #ce9178;">#21716a</span>;</div><div>}</div><div> </div><div><span style="color: #d7ba7d;">.product-menu</span> <span style="color: #d7ba7d;">.panel</span> <span style="color: #d7ba7d;">.panel-header</span> <span style="color: #d7ba7d;">.panel-title</span>><span style="color: #d7ba7d;">.panel-toggler:not</span>(<span style="color: #d7ba7d;">.collapsed</span>),</div><div><span style="color: #d7ba7d;">.product-menu</span> <span style="color: #d7ba7d;">.panel</span> <span style="color: #d7ba7d;">.panel-heading</span> <span style="color: #d7ba7d;">.panel-title</span>><span style="color: #d7ba7d;">.panel-toggler:not</span>(<span style="color: #d7ba7d;">.collapsed</span>) {</div><div> <span style="color: #9cdcfe;">background-color</span>: <span style="color: #ce9178;">#27635d</span>;</div><div>}</div><div> </div><div><span style="color: #d7ba7d;">.product-menu.sidebar</span> <span style="color: #d7ba7d;">.sidebar-header</span> {</div><div> <span style="color: #9cdcfe;">background-color</span>: <span style="color: #ce9178;">#2e837b</span>;</div><div>}</div><div> </div><div><span style="color: #d7ba7d;">#content-wrapper</span> <span style="color: #d7ba7d;">.control-menu-level-1</span> {</div><div> <span style="color: #9cdcfe;">background-color</span>: <span style="color: #ce9178;">#2e837b</span>;</div><div>}</div></div></div></blockquote>
<h2>Bước 5: Sử dụng theme</h2>
<p style="text-align: justify;">Đầu tiên, chúng ta cần thực hiện deploy theme lên service. Chỉ cần copy file *.war vào thư mục deploy và chờ đợi vài phút (deploy theme thường lâu hơn so với portlet thông thường).</p>
<p style="text-align: justify;">Tiếp đến chúng ta cần thực hiện cấu hình để sử dụng thêm. Có hai cách để biến theme mới tạo thành theme mặc định của Liferay (tại giao diện quản trị).</p>
<h3 style="text-align: left;">Cách 1: Sử dụng cấu hình</h3>
<p style="text-align: justify;">Mở file portal-ext.properties trong thư mục cài đặt Liferay (hoặc có thể sử dụng file portal-setup-wizard.properties nếu không tìm thấy). Thêm cấu hình như sau:</p>
<blockquote><i><b>control.panel.layout.regular.theme.id={customer_theme_id}</b></i></blockquote>
<p style="text-align: center;"><i>({customer_theme_id} xem trong file liferay-look-and-feel.xml)</i></p>
<p style="text-align: justify;">Cuối cùng là restart service để Liferay nhận được những thay đổi mới</p>
<h3>Cách 2: Sử dụng giao diện quản trị</h3>
<p style="text-align: justify;">Ta có thể cấu hình bằng giao diện quản trị như sau:</p>
<div>
<ul style="text-align: left;">
<li style="text-align: justify;"> Vào phần quản trị (control panel) → Configuration → Instance setting → Instance configuration</li>
<li style="text-align: justify;">Ở select box Defaut Theme và Default Control Panel Theme chọn theme mà chúng ta muốn.</li>
</ul>
</div>
<p style="text-align: justify;">Và đây là kết quả:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-ptShm1bgCe8/XyAFqnZfp5I/AAAAAAAAC_E/ZiZA5MEOot0nzRLYZT7f3AzMFPK8SCVWgCLcBGAsYHQ/s1539/Change-look-and-feel-of-control-panel-3.png" style="margin-left: 1em; margin-right: 1em;">
<img border="0" data-original-height="730" data-original-width="1539" height="190" src="https://1.bp.blogspot.com/-ptShm1bgCe8/XyAFqnZfp5I/AAAAAAAAC_E/ZiZA5MEOot0nzRLYZT7f3AzMFPK8SCVWgCLcBGAsYHQ/w400-h190/Change-look-and-feel-of-control-panel-3.png" width="400" />
</a>
</div>
<h2 style="clear: both; text-align: justify;">Tổng kết</h2>
<p style="text-align: justify;">Bài viết này là một hướng dẫn đơn giản để thay thế theme mặc định của Liferay ở giao diện quản trị. Tuỳ từng mục đích, chúng ta sẽ thay đổi những thành phần mong muốn như các file css, js, template hoặc thay các file ảnh.</p>
<p style="text-align: justify;">Bạn có thể đọc bài viết gốc tại <a href="https://ignek.com/blog/change-look-and-feel-of-control-panel" rel="nofollow" target="_blank">đây</a>.</p>ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com2tag:blogger.com,1999:blog-324075707410296810.post-56482519814231221612020-07-24T08:00:00.006+07:002020-08-14T09:23:36.483+07:00Java 8: Có nên sử dụng filter của Stream API<p style="text-align: justify;">
Java 8 giới thiệu rất nhiều tính năng mới. Trong đó Stream (luồng) cũng là một tính năng cực kỳ hữu ích và được sử dụng thường xuyên (kết hợp với biểu thức Lambda).</p>
<p style="text-align: justify;">
Cụ thể Stream là gì? Nó là một đối tượng mới của Java được giới thiệu từ phiên bản Java 8, giúp cho việc thao tác trên danh sách (collection) và mảng (array) trở nên dễ dàng và tối ưu hơn.
</p>
<p style="text-align: justify;">
Một Stream đại diện cho một chuỗi các phần tử hỗ trợ các hoạt động tổng hợp tuần tự (sequential) và song song (parallel).
</p>
<div style="text-align: center;"><img alt="Các thao tác của Stream API" border="0" data-original-height="339" data-original-width="1024" height="213" src="https://1.bp.blogspot.com/-vzvn7AJuVO0/XxnBp6a0XjI/AAAAAAAAC9E/fzBgvcTZIXsKk_WGTEUQuEPwGUld-nuUgCLcBGAsYHQ/w640-h213/java8streams.png" title="Các thao tác của Stream API" width="640" /></div><a name='more'></a>
<p style="text-align: left;">
Có thể thấy ngay trong định nghĩa Stream có thể giúp chúng ta thao tác trên danh sách và mảng dễ dàng, tối ưu. Ngoài ra, bản thân tôi nhận thấy sử dụng Stream khiến cho code ngắn gọn và dễ đọc hơn rất nhiều.</p>
<p style="text-align: left;">
Tuy nên, trong quá trình review code của cả những người có kinh nghiệm ( > 5-7 năm) hoặc không có tôi vấn thấy họ không tận dụng hết những tính năng ưu việt này. Sau khi trao đổi với họ, tôi nhận ra rằng bản thân họ đã quá quen với cách viết của Java 7 nên việc chuyển qua cách viết mới của Java 8 chưa quen, đồng thời họ muốn công việc trôi.</p>
<p style="text-align: left;">
Trong bài viết này, tôi lấy ví dụ: có một danh sách các số tự nhiên (0 ... n). Tôi muốn lấy ra danh sách các số chẵn. Tôi sẽ triển khai 2 phong cách code (Java 7 và Java 8), rồi đưa ra một so sánh nho nhỏ:</p>
<p style="text-align: left;"><b>Phong cách Java 7:</b></p>
<blockquote>
<div style="text-align: justify;">
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Long</span>> <span style="color: #9cdcfe;">longs</span>; <span style="color: #6a9955;">//A list</span></div><div>
<span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Long</span>> <span style="color: #9cdcfe;">result</span> = <span style="color: #c586c0;">new</span> <span style="color: #4ec9b0;">ArrayList</span><>();</div><div>
<span style="color: #c586c0;">for</span> (<span style="color: #4ec9b0;">long</span> <span style="color: #9cdcfe;">lValue</span> <span style="color: #c586c0;">:</span> longs) {</div><div><span style="color: #c586c0;"> if</span> (lValue % <span style="color: #b5cea8;">2</span> == <span style="color: #b5cea8;">0</span>) {</div><div><span style="color: #9cdcfe;"><span style="color: #c586c0;"> </span></span><span style="color: #c586c0;"> </span><span style="color: #9cdcfe;">result</span>.<span style="color: #dcdcaa;">add</span>(lValue);</div><div><span style="color: #c586c0;"> </span>}</div><div>}</div></div>
</div>
</blockquote>
<p style="text-align: justify;"><b>Phong cách Java 8:</b></p>
<blockquote>
<div style="text-align: justify;">
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Long</span>> <span style="color: #9cdcfe;">longs</span>; <span style="color: #6a9955;">//A list</span></div><div><span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Long</span>> <span style="color: #9cdcfe;">result</span> = <span style="color: #9cdcfe;">longs</span>.<span style="color: #dcdcaa;">stream</span>()</div><div><span style="color: #c586c0;"> </span>.<span style="color: #dcdcaa;">filter</span>(lValue <span style="color: #569cd6;">-></span> lValue % <span style="color: #b5cea8;">2</span> == <span style="color: #b5cea8;">0</span>)</div><div><span style="color: #c586c0;"> </span>.<span style="color: #dcdcaa;">collect</span>(<span style="color: #9cdcfe;">Collectors</span>.<span style="color: #dcdcaa;">toList</span>());</div></div>
</div>
</blockquote>
<p style="text-align: justify;">
Trước tiên, tôi muốn bạn nói về cảm nhận của mình? Hai đoạn code trên điều cho cùng một kết quả (một danh sách số nguyên chẵn). Nhưng bạn thấy cách nào dễ hiểu, tự nhiên và dễ hiểu hơn? (Tuỳ bạn đánh giá).</p>
<p style="text-align: justify;">
Cái tôi có thể đo đếm được (không dựa vào cảm tính) là thời gian thực hiện và lượng bộ nhớ sử dụng. Tôi thực hiện chạy ứng dụng với kích thước đầu vào là 100, 1000, 10000, 100000, 100000 và được kết quả như sau:</p>
<h2 style="text-align: justify;">Thời gian thực hiện</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-reFBYWEmAnU/XxnH30P_HVI/AAAAAAAAC9g/-uL5CXCMzcIijhl3Fzaq5IhZkIOIa2u8gCLcBGAsYHQ/s494/Tho%25CC%259B%25CC%2580i%2Bgian%2Bthu%25CC%259B%25CC%25A3c%2Bhie%25CC%25A3%25CC%2582n.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="321" data-original-width="494" src="https://1.bp.blogspot.com/-reFBYWEmAnU/XxnH30P_HVI/AAAAAAAAC9g/-uL5CXCMzcIijhl3Fzaq5IhZkIOIa2u8gCLcBGAsYHQ/s320/Tho%25CC%259B%25CC%2580i%2Bgian%2Bthu%25CC%259B%25CC%25A3c%2Bhie%25CC%25A3%25CC%2582n.png" width="320" /></a></div>
<p>Biểu đồ cho thấy, thời gian xử lý theo phong cách Java 7 tăng nhanh hơn so với Java 8 khi kích thước đầu vào tăng lên. Tuy nhiên, với số lượng đầu vào ít (ví dụ: < 1000000) thì chênh lệch không đáng kể.</p>
<h2 style="text-align: justify;">Bộ nhớ sử dụng</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-ZVwn_fXvX0A/XxnIA8x5AII/AAAAAAAAC9k/2KfCd5nGB0gr5z-dHo1P8bqgcmkdSTG_gCLcBGAsYHQ/s569/Bo%25CC%25A3%25CC%2582%2Bnho%25CC%259B%25CC%2581%2Bsu%25CC%259B%25CC%2589%2Bdu%25CC%25A3ng.png" style="margin-left: 1em; margin-right: 1em;">
<img border="0" data-original-height="321" data-original-width="569" src="https://1.bp.blogspot.com/-ZVwn_fXvX0A/XxnIA8x5AII/AAAAAAAAC9k/2KfCd5nGB0gr5z-dHo1P8bqgcmkdSTG_gCLcBGAsYHQ/s320/Bo%25CC%25A3%25CC%2582%2Bnho%25CC%259B%25CC%2581%2Bsu%25CC%259B%25CC%2589%2Bdu%25CC%25A3ng.png" width="320" /> </a>
</div>
<p class="separator" style="clear: both; text-align: justify;">
Đối với bộ nhớ sử dụng, chúng ta thấy có sự trái ngược so với thời gian thực nghĩa. Nghĩa là bộ nhớ sử dụng theo phong cách Java 7 tăng chậm hơn so với Java tam khi kích thước đầu vào tăng lên. Điều này cũng là hợp lý vì được cái này mất cái kia.</p>
<h2 style="clear: both; text-align: justify;">Kết luận</h2>
<p style="text-align: justify;">
Với 2 so sách như trên, chúng ta hoàn toàn có lý do để bảo vệ phong cách viết code của mình (phong cách Java 7 hoặc Java 8). Nhưng theo tôi, phiên bản sau luôn có những cái tiến tốt hơn phiên bản trước và cách viết code của Java 8 cũng tự nhiên và dễ đọc hơn.</p>
<p style="text-align: justify;">Tùy vào hoàn cảnh, khả năng, sở thích bạn có thể chọn phong cách của riêng mình và theo tôi nên giữ nó thống nhất trong toàn bộ dự án.</p>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0Hanoi, Hoàn Kiếm, Hanoi, Vietnam21.0277644 105.8341598-10.328369697129535 70.6779098 52.383898497129536 140.9904098tag:blogger.com,1999:blog-324075707410296810.post-76816752568915174432020-07-22T17:52:00.009+07:002020-08-14T09:27:08.863+07:00Liferay 7.2/DXP: Sử dụng SystemCheckers để theo dõi trạng thái của hệ thống<p style="text-align: justify;">Liferay 7.2 có nhiều tính năng hay so với các phiên bản trước đó. Bài viết này sẽ giới thiệu một trong số chúng, cụ thể đó là SystemCheckers. Trong các phiên bản cũ, đôi khi hệ thống đã được khởi động xong chúng ta không thấy bất kỳ lỗi gì vì nó không được ghi ra log. Tuy nhiên, chúng ta vẫn không sử dụng được một vài service hoặc module. Ví dụ, một vài service được khai báo (Declarative Service) không thể hoạt động do thiếu tham chiếu đến các service/module khác hoặc bị tham chiếu vòng.</p>
<div style="text-align: center;"><img border="0" data-original-height="789" data-original-width="940" height="336" src="https://1.bp.blogspot.com/--Ana8Ilpb_k/XyD3Rjvou5I/AAAAAAAAC_g/5Qti2sM2UScY5KkKl9QTa2z5_pRZfAluACLcBGAsYHQ/w400-h336/system-checker.png" width="400" /></div><a name='more'></a>
<p style="text-align: justify;">Để giải quyết vấn đề này, Liferay giới thiếu tính năng SystemCheckers. Nó sẽ giúp chung ta xác định xem các thành phần trong hệ thống, cái nào hoạt động hoặc là không. SystemCheckers sẽ cung cấp các thông tin hữu ích để chúng ta xác định và xử lý các lỗi có thể xảy ra. Ví dụ lỗi khai báo ở file bnd, tên của component, phiên bản build... bị lỗi. SystemCheckers sẽ đưa ra các vấn đề chung chung cần xem xét. Nhưng nếu bạn muốn xác định một vài vấn đề cụ thể thị bạn cũng có thể thực hiện được bằng cài đặt lại giao diện SystemChecker và đăng ký nó vào hệ thống OSGi.</p>
<p style="text-align: justify;">Bài viết này sẽ chỉ cho bạn làm thế nào để thêm logic cụ thể.</p>
<h2 style="text-align: justify;">Bước 1: Cài đặt giao diện SystemCheckers</h2>
<blockquote><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div style="text-align: justify;">@<span style="color: #4ec9b0;">Component</span>(immediate = <span style="color: #569cd6;">true</span>, service = <span style="color: #9cdcfe;">SystemChecker</span>.<span style="color: #9cdcfe;">class</span>)</div><div style="text-align: justify;"><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">CustomSystemChecker</span> <span style="color: #569cd6;">implements</span> <span style="color: #4ec9b0;">SystemChecker</span> {</div><div style="text-align: justify;">}</div></div></div></blockquote>
<h2 style="text-align: justify;">Bước 2: Ghi đè các phương thức của SystemCheckers</h2>
<blockquote><div style="background-color: #1e1e1e; line-height: 18px;"><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;">@<span style="color: #4ec9b0;">Override</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">String</span> <span style="color: #dcdcaa;">check</span>() {</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #6a9955;"><span style="color: #d4d4d4;"> </span>// Return result of your custom logic</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #c586c0;"><span style="color: #d4d4d4;"> </span>return</span> <span style="color: #ce9178;">"Hello World"</span>;</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;">}</div><div style="text-align: justify;"><span face="" style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; white-space: pre;"><br />
</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;">@<span style="color: #4ec9b0;">Override</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">String</span> <span style="color: #dcdcaa;">getName</span>() {</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #6a9955;"><span style="color: #d4d4d4;"> </span>// Name of your custom System Checker</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #c586c0;"><span style="color: #d4d4d4;"> </span>return</span> <span style="color: #ce9178;">"Hello World Checker"</span>;</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;">}</div><div style="text-align: justify;"><br />
</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;">@<span style="color: #4ec9b0;">Override</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">String</span> <span style="color: #dcdcaa;">getOSGiCommand</span>() {</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #6a9955;"><span style="color: #d4d4d4;"> </span>// Name of your custom osgi command</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #c586c0;"><span style="color: #d4d4d4;"> </span>return</span> <span style="color: #ce9178;">"osgi:helloworld"</span>;</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;">}</div><div style="text-align: justify;"><br />
</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;">@<span style="color: #4ec9b0;">Override</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">String</span> <span style="color: #dcdcaa;">toString</span>() {</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #c586c0;"><span style="color: #d4d4d4;"> </span>return</span> <span style="color: #dcdcaa;">getName</span>();</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;">}<br /></div></div></blockquote>
<p style="text-align: justify;">Sử dụng đoạn code trên bạn có thể cài đặt các nội dung cần kiểm tra của riêng mình và nó sẽ được gọi đến khi bạn gõ lệnh "<b>system:check</b>" ở giao diện <b>gogo shell</b>.</p>
<p style="text-align: justify;">Chú ý: Đoạn code trên sẽ được thực hiện cùng với SystemChecker mặc định của hệ thống. Nếu bạn muốn chỉ chạy riêng phần của bạn thì bạn cần phải tạo ra một câu lệnh OSGi mới. Đoạn code dưới đây sẽ chỉ cho bạn cách để tạo câu lệnh như vậy.</p>
<blockquote><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div style="text-align: justify;">@<span style="color: #4ec9b0;">Component</span>(</div><div style="text-align: justify;"> immediate = <span style="color: #569cd6;">true</span>,</div><div style="text-align: justify;"> property = {<span style="color: #ce9178;">"osgi.command.function=helloworld"</span>, <span style="color: #ce9178;">"osgi.command.scope=osgi"</span>},</div><div style="text-align: justify;"> service = <span style="color: #9cdcfe;">CustomOSGiCommands</span>.<span style="color: #9cdcfe;">class</span></div><div style="text-align: justify;">)</div><div style="text-align: justify;"><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">CustomOSGiCommands</span> {</div><div style="text-align: justify;"><span style="color: #569cd6;"><span style="color: #d4d4d4;"> </span>public</span> <span style="color: #4ec9b0;">void</span> <span style="color: #dcdcaa;">helloworld</span>() {</div><div style="text-align: justify;"><span style="color: #9cdcfe;"><span style="color: #d4d4d4;"> </span></span> <span style="color: #9cdcfe;">System</span>.<span style="color: #9cdcfe;">out</span>.<span style="color: #dcdcaa;">println</span>(<span style="color: #ce9178;">"Hello World"</span>);</div><div style="text-align: justify;"> }</div><div style="text-align: justify;">}<br /></div></div></blockquote>
<p style="text-align: justify;">Với đoạn code như trên, khi bạn gõ lệnh "<b>osgi:helloworld</b>" tại giao diện <b>gogo shell</b>. Phương thức helloworld sẽ được gọi và trả ra kết quả tương ứng.</p>
<h2 style="text-align: justify;">Tổng kết</h2>
<p>Tính năng này thực sự hũu ích. Chúng ta có thể kiểm tra được trạng thái của hệ thống, có thể đảm bảo hệ thống luôn hoạt động tốt nhất.</p>
<p style="text-align: justify;">Bạn có thể đọc bài viết gốc tại <a href="https://www.surekhatech.com/blog/systemcheckers-in-liferay-7-2-dxp" target="_blank">đây</a>.</p>ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0Hanoi, Hoàn Kiếm, Hanoi, Vietnam21.0277644 105.8341598-35.502075219929324 35.521659799999995 77.557604019929315 176.1466598tag:blogger.com,1999:blog-324075707410296810.post-16033925083066907502020-07-21T12:52:00.008+07:002020-08-14T09:34:56.712+07:00Liferay 7.x: Đăng ký thêm loại dữ liệu vào ElasticSearch<p style="text-align: justify;">Như chúng ta đã biết, ElasticSearch là công cụ tìm kiếm mặc định trên Liferay 7/DXP. Nó có nhiều tính năng nổi bật hơn so với Lucene trên các phiên bản Liferay cũ. Liferay sử dụng ElasticSearch cho mục đích tìm kiếm nội bộ. Ví dụ: Tìm kiếm full-tẽt, phân tích lưu trữ, auto-complete, kiểm tra chính tả, khoảng cách địa lý... Hiện tại, chung ta có thể sử dụng tất cả các tính năng này cho các Entity mà mình tạo ra bằng cách kế thừa các indexer của ElasticSearch. Bài viết này sẽ chỉ cho bạn cách dụng ElasticSearch cho các entity tự tạo.</p>
<div style="text-align: center;"><img alt="Đăng ký thêm loại dữ liệu vào ElasticSearch" border="0" data-original-height="788" data-original-width="940" height="334" src="https://1.bp.blogspot.com/-zW5PzUrYxrk/XyD-H8JSb9I/AAAAAAAAC_8/AvunzsaBUHQSZZ90N--EK-bnuW-42pJ0ACLcBGAsYHQ/w400-h334/Liferay%2B%2526%2BElasticSearch.png" title="Đăng ký thêm loại dữ liệu vào ElasticSearch" width="400" /></div><a name='more'></a>
<h2 style="text-align: left;">Các bước triển khai</h2><h3 style="text-align: left;">Bước 1: Tạo mới Entity bằng service builder</h3>
<p style="text-align: justify;">Chúng ta sẽ cài đặt các tính năng của ElasticSearch trên một entity tên là "Deal". Ở đây, chúng ta sẻ dụng service builder để tạo ra một entity theo cách thông thường.</p>
<blockquote><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: grey;"><</span><span style="color: #569cd6;">entity</span> <span style="color: #9cdcfe;">local-service</span>=<span style="color: #ce9178;">"true"</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"Deal"</span> <span style="color: #9cdcfe;">remote-service</span>=<span style="color: #ce9178;">"true"</span> <span style="color: #9cdcfe;">uuid</span>=<span style="color: #ce9178;">"true"</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"dealId"</span> <span style="color: #9cdcfe;">primary</span>=<span style="color: #ce9178;">"true"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"long"</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"title"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"String"</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"description"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"String"</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"image"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"String"</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"categoryId"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"long"</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"price"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"double"</span> <span style="color: grey;">/></span></div><br /><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"groupId"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"long"</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"companyId"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"long"</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"userId"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"long"</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"userName"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"String"</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"createDate"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"Date"</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">column</span> <span style="color: #9cdcfe;">name</span>=<span style="color: #ce9178;">"modifiedDate"</span> <span style="color: #9cdcfe;">type</span>=<span style="color: #ce9178;">"Date"</span> <span style="color: grey;">/></span></div><div><span style="color: grey;"></</span><span style="color: #569cd6;">entity</span><span style="color: grey;">></span></div></div></div></blockquote>
<h3 style="text-align: left;">Bước 2: Tạo một lớp Indexer cho Entity</h3>
<div>
<ul style="text-align: left;">
<li style="text-align: justify;">Để thuận tiện, tốt nhất là tạo lớp Indexer có tên trùng với tên của Entity và tạo một package mới trong tầng service</li>
<li style="text-align: justify;">Lớp mới này được extends từ lớp trừ tượng BaseIndexer.</li>
</ul>
</div>
<blockquote><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div>@<span style="color: #4ec9b0;">Component</span>(immediate = <span style="color: #569cd6;">true</span>, service = <span style="color: #9cdcfe;">Indexer</span>.<span style="color: #9cdcfe;">class</span>)</div><div><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">DealIndexer</span> <span style="color: #569cd6;">extends</span> <span style="color: #4ec9b0;">BaseIndexer</span><<span style="color: #4ec9b0;">Deal</span>> {</div><div> <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">final</span> <span style="color: #4ec9b0;">String</span> <span style="color: #9cdcfe;">CLASS_NAME</span> = <span style="color: #9cdcfe;">DealIndexer</span>.<span style="color: #9cdcfe;">class</span>.<span style="color: #dcdcaa;">getName</span>();</div><div>}</div></div></div></blockquote>
<h3 style="text-align: left;">Bước 3: Cài đặt constructor của lớp DealIndexer</h3>
<div>
<ul style="text-align: left;">
<li style="text-align: justify;"> Tạo mới một constructor, nó sẽ thực hiện chọn các trường mặc định để ElasticSearch gọi đến</li>
<li style="text-align: justify;">Constructor cũng sẽ đặt quyền để đánh index các kết quả. Nếu chúng ta không thực hiện đặt quyền, ElasticSearch sẽ trả lại tất cả các kết quả phù hợp với điều kiện tìm kiếm bất kể quyền của người dùng trên tài nguyên. Vậy tốt nhất là đặt quyền cho nó.</li>
</ul>
</div>
<blockquote><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #569cd6;">public</span> <span style="color: #dcdcaa;">DealIndexer</span>() {</div><div> <span style="color: #dcdcaa;">setDefaultSelectedFieldNames</span>(</div><div> <span style="color: #9cdcfe;">Field</span>.<span style="color: #9cdcfe;">COMPANY_ID</span>, </div><div> <span style="color: #9cdcfe;">Field</span>.<span style="color: #9cdcfe;">ENTRY_CLASS_NAME</span>,</div><div> <span style="color: #9cdcfe;">Field</span>.<span style="color: #9cdcfe;">ENTRY_CLASS_PK</span>,</div><div> <span style="color: #9cdcfe;">Field</span>.<span style="color: #9cdcfe;">GROUP_ID</span>,</div><div> <span style="color: #9cdcfe;">Field</span>.<span style="color: #9cdcfe;">MODIFIED_DATE</span>);</div><div> <span style="color: #dcdcaa;">setPermissionAware</span>(<span style="color: #569cd6;">true</span>);</div><div> <span style="color: #dcdcaa;">setFilterSearch</span>(<span style="color: #569cd6;">true</span>);</div><div>}</div></div></div></blockquote>
<h3 style="text-align: left;">Bước 4: Ghi đè phương thực hasPermission</h3>
<p>Việc ghi đè phương thức hasPermission để đảm bảo người dùng có quyền VIET có thể lấy được kết quả tìm kiếm của ElasticSearch.</p>
<blockquote><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div style="font-family: menlo, monaco, "courier new", monospace; line-height: 18px;"><div>@<span style="color: #4ec9b0;">Override</span></div><div><span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">boolean</span> <span style="color: #dcdcaa;">hasPermission</span>(</div><div> <span style="color: #4ec9b0;">PermissionChecker</span> permissionChecker, <span style="color: #4ec9b0;">String</span> entryClassName, </div><div> <span style="color: #4ec9b0;">long</span> entryClassPK, <span style="color: #4ec9b0;">String</span> actionId) throws Exception {</div><br /><div> <span style="color: #c586c0;">return</span> <span style="color: #dcdcaa;">containsPermissions</span>(permissionChecker, </div><div> entryClassPK, <span style="color: #9cdcfe;">ActionKeys</span>.<span style="color: #9cdcfe;">VIEW</span>);</div><div>}</div></div></div></blockquote>
<h3 style="text-align: left;">Bước 5: Ghi đè phương thức doGetSummary</h3>
<p style="text-align: justify;"> Phương thức doGetSummary sẽ trả lại thông tin tóm tắt của kết quả tìm kiếm. Chúng ta có thể trả lại tên, tiều đề, nội dung... đảm bảo cho người dùng nhận biết được kết quả.</p>
<blockquote><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div>@<span style="color: #4ec9b0;">Override</span></div><div><span style="color: #569cd6;">protected</span> <span style="color: #4ec9b0;">Summary</span> <span style="color: #dcdcaa;">doGetSummary</span>(</div><div> <span style="color: #4ec9b0;">Document</span> document, <span style="color: #4ec9b0;">Locale</span> locale, <span style="color: #4ec9b0;">String</span> snippet,</div><div><div style="font-family: menlo, monaco, "courier new", monospace; line-height: 18px;"><div style="line-height: 18px;"> <span style="color: #4ec9b0;">PortletRequest</span> portletRequest, <span style="color: #4ec9b0;">PortletResponse</span> portletResponse) {</div></div></div><br /><div> <span style="color: #4ec9b0;">Summary</span> <span style="color: #9cdcfe;">summary</span> = <span style="color: #dcdcaa;">createSummary</span>(document);</div><div> <span style="color: #9cdcfe;">summary</span>.<span style="color: #dcdcaa;">setMaxContentLength</span>(<span style="color: #b5cea8;">200</span>);</div><div> <span style="color: #c586c0;">return</span> summary;</div><div>}</div></div></blockquote>
<h3 style="text-align: left;">Bước 6: Ghi đè phương thức doGetDocument</h3>
<p style="text-align: justify;">Phương thức doGetDocumnent là quan trọng nhất của các lớp Indexer. Nó sẽ định nghĩa những thông tin nào sẽ được thêm vào trong ElasticSearch. Phương thức này sẽ được dùng để lấy kết quả từ ElasticSearch.</p>
<blockquote><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div>@<span style="color: #4ec9b0;">Override</span></div><div><span style="color: #569cd6;">protected</span> <span style="color: #4ec9b0;">Document</span> <span style="color: #dcdcaa;">doGetDocument</span>(<span style="color: #4ec9b0;">Deal</span> deal) throws Exception {</div><div> <span style="color: #4ec9b0;">Document</span> <span style="color: #9cdcfe;">document</span> = <span style="color: #dcdcaa;">getBaseModelDocument</span>(<span style="color: #9cdcfe;">Deal</span>.<span style="color: #9cdcfe;">class</span>.<span style="color: #dcdcaa;">getName</span>(), deal);</div><div> </div><div> <span style="color: #6a9955;">// Add column to be indexed</span></div><div> <span style="color: #9cdcfe;">document</span>.<span style="color: #dcdcaa;">addNumber</span>(<span style="color: #ce9178;">"dealId"</span>, <span style="color: #9cdcfe;">deal</span>.<span style="color: #dcdcaa;">getDealId</span>());</div><div> <span style="color: #9cdcfe;">document</span>.<span style="color: #dcdcaa;">addNumber</span>(<span style="color: #ce9178;">"dealPrice"</span>, <span style="color: #9cdcfe;">deal</span>.<span style="color: #dcdcaa;">getPrice</span>());</div><div> <span style="color: #9cdcfe;">document</span>.<span style="color: #dcdcaa;">addNumber</span>(<span style="color: #ce9178;">"dealTitle"</span>, <span style="color: #9cdcfe;">deal</span>.<span style="color: #dcdcaa;">getTitle</span>());</div><div> </div><div> <span style="color: #c586c0;">return</span> document;</div><div>}</div></div></div></blockquote>
<h3 style="text-align: left;">Bước 7: Ghi đè phương thức doReindex</h3>
<p>Chúng ta cần phải cài đặt ba phương thức doReindex</p>
<div><ul style="text-align: left;"><li style="text-align: justify;">Phương thức thứ 1: Nó có duy nhất 01 tham số là một đối tượng của Entity. Phương thức này được gọi khi người quản trị thực hiện reindex từ control panel</li><li style="text-align: justify;">Phương thức thứ 2: Nó có 2 tham số là tên lớp và khoá chính. </li><li style="text-align: justify;">Phương thức thứ 3: Nó có 01 tham số là mảng ID của bảng Company. Nó sẽ reindex tất cả các nội dung thuộc các company.</li></ul></div>
<blockquote><h5 style="background-color: white; color: #41484d; font-family: roboto, sans-serif; font-size: 16px; line-height: 1.38; margin: 20px 0px 6px; padding: 0px; text-rendering: optimizelegibility; transition: all 0.4s ease 0s;"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; font-weight: normal; line-height: 18px; white-space: pre;"><div style="font-family: menlo, monaco, "courier new", monospace; line-height: 18px;"><div>@<span style="color: #4ec9b0;">Override</span></div><div><span style="color: #569cd6;">protected</span> <span style="color: #4ec9b0;">void</span> <span style="color: #dcdcaa;">doReindex</span>(<span style="color: #4ec9b0;">Deal</span> deal) throws Exception {</div><div> <span style="color: #4ec9b0;">Document</span> <span style="color: #9cdcfe;">document</span> = <span style="color: #dcdcaa;">getDocument</span>(deal);</div><div> <span style="color: #9cdcfe;">indexWriterHelper</span>.<span style="color: #dcdcaa;">updateDocument</span>(</div><div> <span style="color: #dcdcaa;">getSearchEngineId</span>(), <span style="color: #9cdcfe;">deal</span>.<span style="color: #dcdcaa;">getCompanyId</span>(), </div><div> document, <span style="color: #dcdcaa;">isCommitImmediately</span>());</div><div>}</div><br /><div>@<span style="color: #4ec9b0;">Override</span></div><div><span style="color: #569cd6;">protected</span> <span style="color: #4ec9b0;">void</span> <span style="color: #dcdcaa;">doReindex</span>(<span style="color: #4ec9b0;">String</span> className, <span style="color: #4ec9b0;">long</span> classPK) throws Exception {</div><div> <span style="color: #4ec9b0;">Deal</span> <span style="color: #9cdcfe;">deal</span> = <span style="color: #9cdcfe;">DealLocalServiceUtil</span>.<span style="color: #dcdcaa;">getDeal</span>(classPK);</div><div> <span style="color: #dcdcaa;">doReindex</span>(deal);</div><div>}</div><br /><div>@<span style="color: #4ec9b0;">Override</span></div><div><span style="color: #569cd6;">protected</span> <span style="color: #4ec9b0;">void</span> <span style="color: #dcdcaa;">doReindex</span>(<span style="color: #4ec9b0;">String</span>[] ids) throws Exception {</div><div> <span style="color: #6a9955;">// here I’ve just reindex single companyIds documents</span></div><div> <span style="color: #4ec9b0;">long</span> <span style="color: #9cdcfe;">companyId</span> = <span style="color: #9cdcfe;">GetterUtil</span>.<span style="color: #dcdcaa;">getLong</span>(ids[<span style="color: #b5cea8;">0</span>]);</div><div> <span style="color: #dcdcaa;">reindexEntries</span>(companyId);</div><div>}</div></div></div></h5></blockquote>
<h3 style="text-align: left;">Bước 8: Ghi đè phương thức doDelete</h3>
<p style="text-align: justify;">Phương thức này sẽ thực hiện xoá các document trong ElasticSearch của một Entity khi Entity đó được xoá.</p>
<blockquote><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div>@<span style="color: #4ec9b0;">Override</span></div><div><span style="color: #569cd6;">protected</span> <span style="color: #4ec9b0;">void</span> <span style="color: #dcdcaa;">doDelete</span>(<span style="color: #4ec9b0;">Deal</span> deal) throws Exception {</div><div> <span style="color: #dcdcaa;">deleteDocument</span>(<span style="color: #9cdcfe;">deal</span>.<span style="color: #dcdcaa;">getCompanyId</span>(), <span style="color: #9cdcfe;">deal</span>.<span style="color: #dcdcaa;">getDealId</span>());</div><div>}</div></div></div></blockquote>
<h2 style="text-align: left;">Tổng kết</h2>
<p style="text-align: justify;">Đến đây, Entity của bạn đã sẵn sàng để ElasticSearch đánh Index. Bất cứ khi nào bạn thêm mới, cập nhật hoặc xoá thì ElasticSearch cũng thực hiện công việc tương ứng. Và nó sẽ xuất hiện trong kết quả tìm kiếm của công cụ.</p>
<p>Bạn có thể đọc bài viết gốc tại <a href="https://www.surekhatech.com/blog/create-custom-entity-in-elasticsearch-for-lr-7-2-dxp" target="_blank">đây</a>.</p>ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-72404809167645847992020-07-19T00:40:00.006+07:002020-08-14T10:52:56.119+07:00Liferay 7.x: Hiểu về Service Tracker<p style="text-align: justify;">Như chúng ta đã biết, Liferay DXP sử dụng framework OSGi cho môi trường cộng tác giữa các đối tượng (các đối tượng ngày được xem như các services). Khai báo các dịch vụ (Declarative Services (DS) - khái niệm quan trọng của OSGi) là cách để chia sẻ các service giữa các thành phần trong hệ thống. Khi chúng ta khai báo một lớp của Java với annotation @Component thì lớp đó được xem như là một service và sẽ được sử dụng bởi các thành phần khác. Việc sử dụng annotation @Component để biến một lớp của Java thành một service thực sự dễ cài đặt và mở rộng.</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-5F6i9XUJcO8/XzYJtQz3RKI/AAAAAAAADDY/A1wMs4lA9dU0ha0bW5vi3zxK683faPlVwCLcBGAsYHQ/s940/Service%2BTracker.png" style="margin-left: 1em; margin-right: 1em;"><img alt="Service Tracker" border="0" data-original-height="788" data-original-width="940" height="336" src="https://1.bp.blogspot.com/-5F6i9XUJcO8/XzYJtQz3RKI/AAAAAAAADDY/A1wMs4lA9dU0ha0bW5vi3zxK683faPlVwCLcBGAsYHQ/w400-h336/Service%2BTracker.png" title="Service Tracker" width="400" /></a></div><div style="text-align: justify;"><br /></div><span>
<a name='more'></a></span>
<p style="text-align: justify;">Để sử dụng một service khác thực sự đơn giản, chỉ cần dùng annotation @Reference để gọi đến service mong muốn. Như vậy, service/lớp hiện tại sẽ phụ thuộc vào các service/lớp mà nó tham chiếu. Khi tất cả các service/lớp được deploy thì trạng thái của service/lớp hiện tại là active (sẵn sàng sử dụng). Nhưng đối với các lớp không có khai báo annotation @Component thì chúng ta không thể gọi đến các service/lớp tham chiếu và annotation @Reference sẽ bị bỏ qua.</p>
<p style="text-align: justify;">Một điều quan trọng nữa là việc sử dụng các lớp tĩnh trong môi trường runtime OSGi là không nên và nó có thể gây ra các lỗi không thể phục hồi vì rất khó để dừng hoặc gỡ các service được gọi bởi các lớp tĩnh.</p>
<p style="text-align: justify;">Việc sử dụng annotation như trên là không khả thi. Do đó, service-tracker được sinh ra để theo dõi rất cả các service trong hệ thống. Nó được sử dụng để theo dõi việc thêm, sửa hoặc xoá các service và rất nhiều các thao tác điều khiển khác nữa.</p>
<p style="text-align: justify;">Hãy lấy một ví dụ đơn giản được sử dụng trong Liferay 7.x service builder (các lớp XxxLocalServiceUtil).</p>
<blockquote><div style="background-color: #1e1e1e; line-height: 18px;"><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #4ec9b0;">DealLocalService</span> <span style="color: #dcdcaa;">getService</span>() {</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">_serviceTracker</span>.<span style="color: #dcdcaa;">getService</span>(); </div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;">} </div><div style="text-align: justify;"><span face="" style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; white-space: pre;"><br /></span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #569cd6;">private</span> <span style="color: #569cd6;">static</span> <span style="color: #4ec9b0;">ServiceTracker</span><DealLocalService, DealLocalService> <span style="color: #4ec9b0;">_serviceTracker</span> </div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> = <span style="color: #9cdcfe;">ServiceTrackerFactory</span>.<span style="color: #dcdcaa;">open</span>(<span style="color: #9cdcfe;">DealLocalService</span>.<span style="color: #9cdcfe;">class</span>);</div></div></blockquote>
<p style="text-align: justify;">Đối với các lớp khác muốn khai báo service-tracker</p>
<blockquote><div style="background-color: #1e1e1e; line-height: 18px;"><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #4ec9b0;">Bundle</span> <span style="color: #9cdcfe;">bundle</span> = <span style="color: #9cdcfe;">FrameworkUtil</span>.<span style="color: #dcdcaa;">getBundle</span>(<span style="color: #569cd6;">this</span>.<span style="color: #dcdcaa;">getClass</span>());</div><div style="text-align: justify;"><span face="" style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; white-space: pre;"><br /></span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #4ec9b0;">BundleContext</span> <span style="color: #9cdcfe;">bundleContext</span> = <span style="color: #9cdcfe;">bundle</span>.<span style="color: #dcdcaa;">getBundleContext</span>();</div><div style="text-align: justify;"><br /></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #4ec9b0;">ServiceTracker</span><DealLocalService, DealLocalService> serviceTracker </div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> = <span style="color: #c586c0;">new</span> <span style="color: #dcdcaa;">ServiceTracker</span>(bundleContext, <span style="color: #9cdcfe;">DealLocalService</span>.<span style="color: #9cdcfe;">class</span>, <span style="color: #569cd6;">null</span>);</div><div style="text-align: justify;"><br /></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #9cdcfe;">serviceTracker</span>.<span style="color: #dcdcaa;">open</span>();</div><div style="text-align: justify;"><br /></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">serviceTracker</span>.<span style="color: #dcdcaa;">getService</span>();</div></div></blockquote>
<p style="text-align: justify;">Sau khi khai báo service-tracker, nó cần được khởi tạo trước khi sử dụng. Và cuối cùng, khi gỡ ứng dụng, service-tracker sẽ gọi phương thức close() để kết thúc.</p>
<p style="text-align: justify;">Khi sử dụng Service Tracker để theo dõi trạng thái của service. Nếu phương thực getService() trả về <i>null </i>nghĩa là service chưa được deploy. Nếu thấy nhiều instance thì có thể sử dụng một trong số chúng.</p>
<h2 style="text-align: justify;">Tuỳ biến theo dõi service</h2>
<p style="text-align: justify;">Chúng ta có thể sử dụng ServiceTrackerCustomizer cho nhiều mục đích. Ví dụ: Xem danh sách các service đang hoạt động chẳng hạn. Nhưng ở đây, chúng ta sẽ làm những cái cụ thể hơn. Các service trong OSGi có thể khác nhau, nhưng chúng có điều giống là đều sử dụng annotation @Component. Bởi vậy, chúng ta có thể dùng ServiceTrackerCustomizer để lấy được những chi tiết của service.</p>
<p style="text-align: justify;">Để sử dụng ServiceTrackerCustomizer, chúng ta cần tạo ra một lớp mới và ghi đè các phương thức đã có: addingService, modifiedService và removedService. Để xoá các tài nguyên khi service được gỡ bỏ, phương thức removedService cần được sử dụng sau khi service được xoá bỏ khỏi bộ nhớ của OSGi.</p>
<blockquote><div><div style="background-color: #1e1e1e; line-height: 18px;"><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">DealServiceTrackerCustomizer</span> </div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #569cd6;">implements</span> <span style="color: #4ec9b0;">ServiceTrackerCustomizer</span><<span style="color: #4ec9b0;">DealLocalService</span>, <span style="color: #4ec9b0;">DealLocalService</span>>{</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> </div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">final</span> <span style="color: #4ec9b0;">BundleContext</span> <span style="color: #9cdcfe;">bundleContext</span>;</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #dcdcaa;">DealServiceTrackerCustomizer</span>(<span style="color: #4ec9b0;">BundleContext</span> <span style="color: #9cdcfe;">bundleContext</span>) {</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #569cd6;">this</span>.<span style="color: #9cdcfe;">bundleContext</span> = bundleContext;</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> }</div><div style="text-align: justify;"><span face="" style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; white-space: pre;"><br /></span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> @<span style="color: #4ec9b0;">Override</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">DealLocalService</span> <span style="color: #dcdcaa;">addingService</span>(</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #4ec9b0;">ServiceReference</span><<span style="color: #4ec9b0;">DealLocalService</span>> <span style="color: #9cdcfe;">reference</span>) {</div><div style="text-align: justify;"><br /></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #4ec9b0;">String</span> <span style="color: #9cdcfe;">dealType</span> = (String) <span style="color: #9cdcfe;">reference</span>.<span style="color: #dcdcaa;">getProperty</span>(<span style="color: #ce9178;">"deal-type"</span>);</div><div style="text-align: justify;"><br /></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #4ec9b0;">DealLocalService</span> <span style="color: #9cdcfe;">service</span> = <span style="color: #9cdcfe;">bundleContext</span>.<span style="color: #dcdcaa;">getService</span>(reference);</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> </div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #c586c0;">if</span>(<span style="color: #9cdcfe;">dealType</span>.<span style="color: #dcdcaa;">equals</span>(DEAL_TYPE_ONLINE)){</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #c586c0;">return</span> service;</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> }<span style="color: #c586c0;">else</span>{</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">null</span>;</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> }</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> </div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> }</div><div style="text-align: justify;"><br /></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> @<span style="color: #4ec9b0;">Override</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">void</span> <span style="color: #dcdcaa;">modifiedService</span>(</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #4ec9b0;">ServiceReference</span><<span style="color: #4ec9b0;">DealLocalService</span>> <span style="color: #9cdcfe;">reference</span>, <span style="color: #4ec9b0;">DealLocalService</span> <span style="color: #9cdcfe;">service</span>) {</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #6a9955;">//This method is called </span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #6a9955;">//when a service has had it properties modified</span></div><div style="text-align: justify;"><br /></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> }</div><div style="text-align: justify;"><br /></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> @<span style="color: #4ec9b0;">Override</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">void</span> <span style="color: #dcdcaa;">removedService</span>(</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #4ec9b0;">ServiceReference</span><<span style="color: #4ec9b0;">DealLocalService</span>> <span style="color: #9cdcfe;">reference</span>, <span style="color: #4ec9b0;">DealLocalService</span> <span style="color: #9cdcfe;">service</span>) {</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #6a9955;">//This method is called after </span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #6a9955;">//a service is no longer being tracked</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> <span style="color: #6a9955;">//code related to clean up related resources can be added here</span></div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;"> }</div><div style="color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; text-align: justify; white-space: pre;">}</div></div></div></blockquote>
<p style="text-align: justify;">Và cuối cùng, chúng ta chỉ sử dụng lớp ServiceTrackerCustomizer thay vì ServiceTracker như đoạn code dưới đây:</p>
<blockquote><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: menlo, monaco, "courier new", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div style="text-align: justify;"><span style="color: #4ec9b0;">ServiceTracker</span><<span style="color: #4ec9b0;">DealLocalService</span>, <span style="color: #4ec9b0;">DealLocalService</span>> <span style="color: #9cdcfe;">serviceTracker</span> =</div><div style="text-align: justify;"> <span style="color: #c586c0;">new</span> <span style="color: #dcdcaa;">ServiceTracker</span>(</div><div style="text-align: justify;"> bundleContext, <span style="color: #9cdcfe;">DealLocalService</span>.<span style="color: #9cdcfe;">class</span>, </div><div style="text-align: justify;"> <span style="color: #c586c0;">new</span> <span style="color: #dcdcaa;">DealServiceTrackerCustomizer</span>(bundleContext));</div></div></div></blockquote>
<p style="text-align: justify;">Bạn có thể xem bài viết gốc tại <a href="https://www.surekhatech.com/blog/liferay-service-tracker" target="_blank">đây</a>.</p>ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0Hanoi, Hoàn Kiếm, Hanoi, Vietnam21.0277644 105.8341598-50.737857025877418 -34.790840200000005 90 -113.54084019999999tag:blogger.com,1999:blog-324075707410296810.post-25370883216166886542020-07-18T14:24:00.002+07:002020-08-14T11:05:17.135+07:00Liferay 7.x: Liên kết đến tài nguyên tĩnh (hình ảnh/js/css) trong portlet <div style="text-align: justify;">Với Liferay DXP, cách thức phát triển portlet/module có sự thay đổi lớn so với các phiên bản Liferay trước đó. Trước đây, chung ta có để dễ dàng sử dụng các tài nguyên tĩnh như CSS, JS hay các hình ảnh. Các tài nguyên này được xem là một phần của các portlet. Nhưng với Liferay DXP, cách tiếp cận là hoàn toàn khác biệt. Hay xem, làm thế nào để sử dụng được các tài nguyên tĩnh trong việc phát triển portlet/module.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-jcUcGzHt8TE/XzYNamchuxI/AAAAAAAADD0/AUfFU3EcMIcqihRPlkhTVuHb88fOWtPJACLcBGAsYHQ/s940/_static%2Bresources.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="788" data-original-width="940" height="336" src="https://1.bp.blogspot.com/-jcUcGzHt8TE/XzYNamchuxI/AAAAAAAADD0/AUfFU3EcMIcqihRPlkhTVuHb88fOWtPJACLcBGAsYHQ/w400-h336/_static%2Bresources.png" width="400" /></a></div><div style="text-align: justify;"><span><a name='more'></a></span></div><div style="text-align: justify;">Đầu tiền, bạn cần phải thêm đường dẫn đến web-context vào file bnd (Ví dụ: tên module là my-custom-portlet).</div><div style="text-align: justify;"><blockquote><i><b>Web-ContextPath: /my-custom-portlet</b></i></blockquote></div><div style="text-align: justify;">Tiếp theo, chúng ta thêm các thư mục chứa tài nguyên vào trong thư mục META-INF (thư mục chứa các file css, các file hình ảnh,...).</div><div style="text-align: justify;"></div><blockquote><div style="text-align: justify;"><i><b>main\resources\META-INF\resources\css</b></i></div><div style="text-align: justify;"><i><b>main\resources\META-INF\resources\images</b></i></div></blockquote><div style="text-align: justify;">Và lúc này, bạn có thể sử dụng các file css, hình ảnh mà các bạn thêm vào. Cụ thể:</div><ul style="text-align: left;"><li>Với css: <i><b><link href="/o/my-custom-portlet/css/my-custom.css" rel="stylesheet" type="text/css" /></b></i></li></ul><ul style="text-align: left;"><li>Với hình ảnh: <i><b><img src="/o/my-custom-portlet/images/logo.jpg" /></b></i></li></ul><div>Bạn có thể làm tương tự với các tài nguyên khác như các file JS, các mẫu báo cáo,...</div><div>Xem bài viết gốc tại <a href="https://itsliferay.blogspot.com/2017/03/include-static-resources-liferaydxp.html" target="_blank">đây</a>.</div>ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-58065994706195919022019-05-26T18:00:00.002+07:002020-08-14T11:58:01.656+07:00Liferay 7: Error while buildCSS<div class="separator" style="clear: both; text-align: center;"></div>To day, I open my old wars project and build it with Gradle. I received the error message like this:<br />
<br />
<span face="" style="font-family: "courier new", courier, monospace;">Was passed main parameter 'sass.append.css.import.timestamps=true' but no main parameter was defined in your arg class</span><br />
<span face="" style="font-family: "courier new", courier, monospace;">:wars:announcement:buildCSS</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"></span><br /><div style="text-align: center;"><a href="https://1.bp.blogspot.com/-b2NqkvP5p0w/XzYZWww4sOI/AAAAAAAADEQ/KhypFie9xzkUexJXWV9slq0BXFHE_uJnwCPcBGAYYCw/s940/build%2BCSS.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="788" data-original-width="940" height="336" src="https://1.bp.blogspot.com/-b2NqkvP5p0w/XzYZWww4sOI/AAAAAAAADEQ/KhypFie9xzkUexJXWV9slq0BXFHE_uJnwCPcBGAYYCw/w400-h336/build%2BCSS.png" width="400" /></a></div>
<a name='more'></a><br />
<span face="" style="font-family: "courier new", courier, monospace;">Usage: java -jar com.liferay.css.builder-3.0.0.jar [options]</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Options:</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> --append-css-import-timestamps</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Whether to append the current timestamp to the URLs in the @import CSS </span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> at-rules. </span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Default: true</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> --base-dir</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> The base directory that contains the SCSS files to compile.</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Default: src/META-INF/resources</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> --compiler</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> The type of Sass compiler to use. Supported values are "jni" and "ruby". </span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> The Ruby Sass compiler requires "com.liferay.sass.compiler.ruby.jar", </span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> "com.liferay.ruby.gems.jar", and "jruby-complete.jar" to be added to the </span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> classpath. </span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Default: jni</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> --dir-names</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> The name of the directories, relative to base directory, which contain </span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> the SCSS files to compile. All sub-directories are searched for SCSS </span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> files as well.</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Default: [/]</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> --excludes</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> The SCSS file patterns to exclude from compiling.</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Default: [**/_diffs/**, **/.sass-cache*/**, **/.sass_cache_*/**, **/_sass_cache_*/**, **/_styled/**, **/_unstyled/**, **/css/aui/**, **/css/clay/**, **/tmp/**]</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> --generate-source-map</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Whether to generate source maps for easier debugging.</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Default: false</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> -h, --help</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Print this message.</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> --import-paths</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> The import directories of Sass libraries.</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> --output-dir</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> The name of the sub-directories where the SCSS files are compiled to. </span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> For each directory that contains SCSS files, a sub-directory with this </span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> name is created.</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Default: .sass-cache/</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> --precision</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> The numeric precision of numbers in Sass.</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Default: 9</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> --rtl-excluded-path-regexps</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> The SCSS file patterns to exclude when converting for right-to-left </span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> (RTL) support.</span><br />
<span face="" style="font-family: "courier new", courier, monospace;"> Default: []</span><br />
<span style="font-family: inherit;">I didn't know why it occur because the last time I built, it still work fine.</span><br />
<span style="font-family: inherit;">After an hour searching on the internet, I realized there was a new version of css builder which was release few days ago.</span><br />
<span style="font-family: inherit;"><br /></span>
The last version of css builder is 3.0.0 and it did not work with the configurations of Gradle. So, the easiest way to fix it is using the previous one.<br />
<br />
To use a specify version of css builder we need to add line:<br />
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: menlo; font-size: 9pt;">cssBuilder <span style="color: #6a8759;">group</span>: <span style="color: #6a8759;">"com.liferay"</span>, <span style="color: #6a8759;">name</span>: <span style="color: #6a8759;">"com.liferay.css.builder"</span>, <span style="color: #6a8759;">version</span>: <span style="color: #6a8759;">"2.1.3"</span></pre>
into the dependences sector at file build.gradle and rebuild your project.<br />
<br />
Thanks!ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com5tag:blogger.com,1999:blog-324075707410296810.post-61812446461541683842017-06-19T23:53:00.001+07:002020-07-18T01:26:29.415+07:00Liferay 7: Service builder và truy vấn dữ liệu<div style="text-align: justify;">
<a href="https://3.bp.blogspot.com/-rryU2YhW6cE/WUgEl5m8zYI/AAAAAAAACaU/iaDWPuKFJI4lwVK3cPLTnx_hDAbTa5NpgCLcBGAs/s1600/cover%2Bcopy.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="150" data-original-width="200" src="https://3.bp.blogspot.com/-rryU2YhW6cE/WUgEl5m8zYI/AAAAAAAACaU/iaDWPuKFJI4lwVK3cPLTnx_hDAbTa5NpgCLcBGAs/s1600/cover%2Bcopy.png" /></a>Trong bài viết <a href="http://chingovan.blogspot.com/2017/04/liferay-7-tao-mot-portlet-moi-nhu-nao.html">Liferay 7: Tạo một portlet mới như thế nào?</a>, mình đã hướng dẫn cách tạo một portlet (mvc portlet) trên môi trường <i>Liferay 7</i>. Như bạn thấy, việc tạo mới một portlet trên môi trường <i>Liferay 7</i> cũng không thực sự khó khăn và mất nhiều thời gian. Tuy nhiên, một ứng dụng không chỉ đơn giản như thế, trong bài viết này mình sẽ đi hướng dẫn cách tương tác với cơ sở dữ liệu (CSDL). Cụ thể là sẽ lấy dữ liệu từ CSDL và hiển thị lên trình duyệt.</div>
<div style="text-align: justify;">
Đồng thời, mình cũng đưa ra so sánh cách tổ chức các module, các lớp, interface của phiên bản 7 so với phiên bản 6.x (xem tại <a href="http://chingovan.blogspot.com/2015/06/liferay-thao-tac-voi-co-so-du-lieu.html" target="_blank">đây</a>).</div>
<a name='more'></a><h2>
Bài toán</h2>
<div style="text-align: justify;">
Trong bài hướng dẫn này, mình sẽ tạo 2 module gồm: 1) module hiển thị dữ liệu tên là <i>database-web</i> và 2)module tương tác với CSDL là <i>database-service</i>.</div>
<div>
<ul>
<li><i>module database-service</i> sẽ chịu trách nhiệm tương tác với CSDL để truy vấn những dữ liệu cần thiết.</li>
<li><i>module database-web</i> sẽ tương tác với <i>module database-service</i> để lấy dữ liệu và hiển thị lên trình duyệt.</li>
</ul>
</div>
<h2>
Liferay Workspace</h2>
<div style="text-align: justify;">
<i>Liferay Workspace (LW)</i> là một khái niệm mới của Liferay (tham khảo tại <a href="http://a%20liferay%20workspace%20is%20a%20generated%20environment%20that%20is%20built%20to%20hold%20and%20manage%20your%20liferay%20projects.%20this%20workspace%20is%20intended%20to%20aid%20in%20the%20management%20of%20liferay%20projects%20by%20providing%20various%20gradle%20build%20scripts%20and%20configured%20properties.%20this%20is%20the%20official%20way%20to%20create%20liferay%20portal%20ce%207.0%20modules%20using%20gradle.%20for%20those%20developers%20that%20still%20want%20to%20develop%20war-style%20plugins%20using%20the%20plugins%20sdk%2C%20this%20way%20is%20also%20supported%20using%20a%20liferay%20workspace./" target="_blank">đây</a>). Ta hiểu đơn giản, <i>LW </i>được tạo ra để quản lý các project. Nhưng tại sao ở phiên bản 7 mới xuất hiện? Theo mình, nguyên nhân là do tính module hoá cao trong <i>Liferay 7</i>, việc module hoá cao dẫn đến việc liên kết giữa các module/project khó khăn. Do đó, Liferay đã tạo ra <i>LW </i>để đơn gian hoá việc liên kết giữa các module với nhau.</div>
<div style="text-align: justify;">
<i>LW </i>là cách chính thức (được Liferay hỗ trợ) để tạo và quản lý các module/project được build bằng <i>Gradle</i>. Còn nếu muốn sử dụng Plugin SDK để tạo ra các module/project thì LW vẫn hỗ trợ. Nếu không có yêu cầu thì đặc biệt thì ta cứ sử dụng theo khuyến nghị của nhà sản xuất :).</div>
<div style="text-align: justify;">
Đầu tiên, ta sẽ tạo mới một <i>LW </i>để xem cấu trúc cụ thể của một <i>LW </i>gồm những gì. Trên <i>IDE Eclipse</i>, bạn chọn <i>File > New > Liferay Workspace Project</i> và điền nội dung như hình dưới:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-Ssgi4ubieAE/WT7SWCV-tAI/AAAAAAAACYw/mmUXdDqsjKUp-gefoIArZ75rmhdX82OuQCLcB/s1600/liferay-workspace.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="495" data-original-width="512" height="309" src="https://4.bp.blogspot.com/-Ssgi4ubieAE/WT7SWCV-tAI/AAAAAAAACYw/mmUXdDqsjKUp-gefoIArZ75rmhdX82OuQCLcB/s320/liferay-workspace.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
Trong bài này, mình đặt tên <i>LW </i>là <i>database-workspace</i>. <i><span style="color: #990000;">Lưu ý, bạn chỉ có thể tạo ra duy nhất một LW trong một IDE (Eclipse, Liferay Studio) workspace</span></i>. Nhấn nút <i>Next</i> để hệ thống tự động tạo các file, thư mục cần thiết.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-5MhLEL8nWqY/WT7S9_nzYVI/AAAAAAAACY4/w_wYhvPKG_4u_1VVDoXmLhxGNos3HVbiACLcB/s1600/liferay-workspace-structure.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="201" data-original-width="268" src="https://4.bp.blogspot.com/-5MhLEL8nWqY/WT7S9_nzYVI/AAAAAAAACY4/w_wYhvPKG_4u_1VVDoXmLhxGNos3HVbiACLcB/s1600/liferay-workspace-structure.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: justify;">
Hình trên là cấu trúc thư mục của một <i>LW</i>. Trong đó, </div>
<div class="separator" style="clear: both; text-align: justify;">
</div>
<ul>
<li>Thư mục modules sẽ chứa các module như portlet, database, persistence, ... </li>
<li>Thư mục config chứa các cấu hình cho các môi trường dev, local, prod và uat, khi triển khai trên môi trường nào thì ta sẽ chọn cấu hình của môi trường đó --> thuận tiện quá. </li>
<li>Thư mục themes sẽ chứa các theme và </li>
<li>Thư mục wars sẽ chứa các file war được build từ các module thích hợp.</li>
</ul>
<div>
<div style="text-align: justify;">
Như vậy, ta đã tạo được môi trường <i>LW</i>. Tiếp theo, ta sẽ tiến thành tạo các module phục vụ việc hiển thị dữ liệu và tương tác với CSDL.</div>
</div>
<h2>
Module tương tác với CSDL</h2>
<div>
<div style="text-align: justify;">
<i>Service Builder</i> là một công cụ mạnh mẽ của Liferay, giúp developer tiết kiệm rất nhiều công sức. Mình sẽ sử dụng lại dữ liệu của bài viết <a href="http://chingovan.blogspot.com/2015/06/liferay-thao-tac-voi-co-so-du-lieu.html" title="Liferay 6.2: Thao tác với cơ sở dữ liệu">Liferay 6.2: Thao tác với cơ sở dữ liệu</a> để tiết kiệm thời gian và công sức. Cụ thể, mình sẽ tạo ra một bảng tên là Student với các trường cơ bản như: mã (code), tên (fullname), ngày sinh (birthday), giới tính (gender) và địa chỉ (address).</div>
<div style="text-align: justify;">
Các bước để tạo một module tương tác với CSDL được thực hiện như sau: Từ <i>Eclipse IDE</i>, chọn <i>File > New > Liferay Module Project</i> được như hình dưới:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-BaPQ3eC_3qA/WT7YTD-8luI/AAAAAAAACZQ/5kRDvx0fb-QBJY8lUV4dGvOEnrA8EfLHgCLcB/s1600/database-servicebuilder.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="497" data-original-width="511" height="311" src="https://4.bp.blogspot.com/-BaPQ3eC_3qA/WT7YTD-8luI/AAAAAAAACZQ/5kRDvx0fb-QBJY8lUV4dGvOEnrA8EfLHgCLcB/s320/database-servicebuilder.png" width="320" /></a></div>
<div style="text-align: justify;">
Mình đặt tên project này là <i>database</i>, kiểu build là <i>Gradle </i>và chọn templage project là <i>service-builder</i>. Nhấn nút <i>Next </i>để chuyển sang bước tiếp theo.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-QP3l5-s3n1Q/WT7YyKwvvCI/AAAAAAAACZU/5I_kEsUw9eENfahJ5Ar0NIx7uvoUm6o4ACLcB/s1600/database-servicebuilder-package.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="494" data-original-width="512" height="308" src="https://4.bp.blogspot.com/-QP3l5-s3n1Q/WT7YyKwvvCI/AAAAAAAACZU/5I_kEsUw9eENfahJ5Ar0NIx7uvoUm6o4ACLcB/s320/database-servicebuilder-package.png" width="320" /></a></div>
<div style="text-align: justify;">
Tại bước này, mình đặt tên cho lớp controller (cụ thể là <i>DatabasePortlet</i>) và tên gói (cụ thể là <i>com.blogspot.chingovan.database</i>) và nhấn nút Finish để kết thúc việc tạo mới porject.</div>
<div style="text-align: justify;">
Lúc này, chúng ta thấy có 03 <i>module </i>mới được sinh ra gồm module <i>database</i>, <i>database-api</i> và <i>database-service</i>, cấu trúc như hình sau:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-dmxUNjfjT_4/WT7ZW_mPjfI/AAAAAAAACZY/-UMhqodQigoX2mDITiCkbwlp2xQUAKD_ACLcB/s1600/database-servicebuilder-structure.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="486" data-original-width="304" height="320" src="https://1.bp.blogspot.com/-dmxUNjfjT_4/WT7ZW_mPjfI/AAAAAAAACZY/-UMhqodQigoX2mDITiCkbwlp2xQUAKD_ACLcB/s320/database-servicebuilder-structure.png" width="200" /></a></div>
<div style="text-align: justify;">
Như trong hình, ta thấy module <i>database </i>sẽ chứa 2 module còn lại. Ta có thể dễ dàng đoán được là module <i>database-api</i> sẽ chứa các <i>interface (api)</i> còn module <i>database-service</i> sẽ chứa các <i>implement </i>của <i>database-api</i>. Chi tiết mình sẽ bàn trong một bài viết khác, ở bài viết này ta chỉ tập trung vào một công việc đơn giản là hiển thị dữ liệu trong CSDL lên màn hình là được.</div>
<div style="text-align: justify;">
Bạn mở module <i>database-service</i>, ta sẽ thấy file <i>service.xml</i>. Mở file này và khai báo một <i>entity </i>tên là <i>Student </i>(hãy xem chi tiết tại <a href="https://github.com/programmerit/database/blob/master/database/database-service/service.xml" target="_blank">đây</a>).</div>
<div style="text-align: justify;">
Đối với phiên bản 6.x, ta sử dụng <i>Plugin SDK</i> để build file<i> service.xml</i>. Đối với <i>Liferay 7</i>, việc dùng <i>Gradle </i>để build file <i>service.xml</i> cũng hoàn toàn tương tự. Bạn chuyển sang view <i>Gradle Tasks</i> (<i>Windows > Showw view > Other > Gradle > Gradle Tasks</i>). Click chuột phải vào mục <i>buildService </i>và chọn <i>Run Gradle Task</i> như hình dưới:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-xzECO6zzcRg/WT7bOYJYGyI/AAAAAAAACZg/bR9OZWLjEcUbuSdGTUCmrGtTvUTHjBvtgCLcB/s1600/database-servicebuilder-grandle-build.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="394" data-original-width="372" height="320" src="https://2.bp.blogspot.com/-xzECO6zzcRg/WT7bOYJYGyI/AAAAAAAACZg/bR9OZWLjEcUbuSdGTUCmrGtTvUTHjBvtgCLcB/s320/database-servicebuilder-grandle-build.png" width="302" /></a></div>
<div style="text-align: justify;">
Đợi <i>Gradle </i>build xong, ta sẽ có cấu trúc của module <i>database-api</i> như sau:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Fop8_U19rlM/WT7bhu1DeBI/AAAAAAAACZk/rOs1K77xfqsNEXOjQeRd1K2gVqEhnxyQACLcB/s1600/database-servicebuilder-api-generated-structure.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="543" data-original-width="396" height="400" src="https://2.bp.blogspot.com/-Fop8_U19rlM/WT7bhu1DeBI/AAAAAAAACZk/rOs1K77xfqsNEXOjQeRd1K2gVqEhnxyQACLcB/s400/database-servicebuilder-api-generated-structure.png" width="291" /></a></div>
<div style="text-align: justify;">
và cấu trúc của module <i>database-service</i> sẽ như sau:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-M_8MHsUXiko/WT7broiT86I/AAAAAAAACZo/ie7jZa53NksmCb0-_5U8BSNC45F8BCVnwCLcB/s1600/database-servicebuilder-service-generated-structure.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="736" data-original-width="436" height="400" src="https://1.bp.blogspot.com/-M_8MHsUXiko/WT7broiT86I/AAAAAAAACZo/ie7jZa53NksmCb0-_5U8BSNC45F8BCVnwCLcB/s400/database-servicebuilder-service-generated-structure.png" width="236" /></a></div>
<div style="text-align: justify;">
Như vậy là ta đã xây dựng xong các module tương tác với CSDL. Tiếp theo, ta sẽ xây dựng module hiển thị dữ liệu bằng cách sử dụng các module vừa xây dựng để truy vấn dữ liệu và hiển thị lên trình duyệt.</div>
</div>
<h2>
Module hiển thị</h2>
<div>
<div style="text-align: justify;">
Để đơn giản, mình sẽ tạo một porlet dạng <i>mvc-portlet</i> để đại diện cho module hiển thị dữ liệu. Để tạo mới một <i>mvc-portlet</i>, bạn chọn <i>File > New > Liferay Module Project </i>như hình dưới:</div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-hOMHPnfRs1o/WT7U2AHRoPI/AAAAAAAACY8/h5bo_93fihwLpnHoKwQHgSVre74u2dergCLcB/s1600/database-mvc-portlet.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="493" data-original-width="510" height="309" src="https://3.bp.blogspot.com/-hOMHPnfRs1o/WT7U2AHRoPI/AAAAAAAACY8/h5bo_93fihwLpnHoKwQHgSVre74u2dergCLcB/s320/database-mvc-portlet.png" width="320" /></a></div>
<div>
<br /></div>
<div style="text-align: justify;">
Mình đặt tên project là <i>database-web</i>, sử dụng Gradle để build với project mẫu là <i>mvc-portlet</i>. Nhấn nút <i>Next</i> để chuyển bước tiếp theo.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-IEDeCnBTqtk/WT7VNtqOvyI/AAAAAAAACZA/fxO6cL3fytogcqqJ9sSmhYXXX20CTqqewCLcB/s1600/database-mvc-portlet-package.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="494" data-original-width="513" height="308" src="https://2.bp.blogspot.com/-IEDeCnBTqtk/WT7VNtqOvyI/AAAAAAAACZA/fxO6cL3fytogcqqJ9sSmhYXXX20CTqqewCLcB/s320/database-mvc-portlet-package.png" width="320" /></a></div>
<div style="text-align: justify;">
Cũng tương tự như <i>Liferay 6.x</i>, ta cần cung cấp tên lớp controller (ở đây là <i>DatabasePortlet</i>) và tên gói (ở đây là <i>com.blogspot.chingovan.database</i>). Tiếp theo, nhấn nút <i>Finish </i>để kết thúc việc tạo mới module hiển thị.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-PMaDNiK7ko4/WT7V6IvrGhI/AAAAAAAACZI/OaFFyO4rxMgvCzkFZlwG_usOCr0zERvcgCLcB/s1600/database-mvc-portlet-structure.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="326" data-original-width="275" height="320" src="https://2.bp.blogspot.com/-PMaDNiK7ko4/WT7V6IvrGhI/AAAAAAAACZI/OaFFyO4rxMgvCzkFZlwG_usOCr0zERvcgCLcB/s320/database-mvc-portlet-structure.png" width="269" /></a></div>
<div style="text-align: justify;">
Hình trên là cấu trúc của module <i>database-web</i>, chúng ta thấy có controller <i>DatabasePortlet </i>và các file <i>*.jsp</i> để sinh mã HTML.</div>
<div style="text-align: justify;">
Mình sẽ sửa nội dung file<i> init.jsp</i> (xem tại <a href="https://github.com/programmerit/database/blob/master/database-web/src/main/resources/META-INF/resources/init.jsp" target="_blank">đây</a>) và file <i>view.jsp</i> phục vụ hiển thị danh sách tất cả các bản ghi trong bảng <i>Student</i> lên màn hình trình duyệt. Nội dung cụ thể như sau:</div>
<script src="https://gist.github.com/programmerit/aeed6277d9d2b6455ae4bb43a9ffc1b0.js"></script><br />
<div style="text-align: justify;">
Trong file <i>view.jsp</i> ta có sử dụng các lớp <i>Student</i> thuộc module <i>database-api</i> và <i>StudentLocalServiceUtil </i>thuộc module <i>database-service</i>. Để gọi được các lớp này, ta cần phải import 2 module trên vào module <i>database-web</i>. Việc import rất đơn giản, bạn chỉ cần thêm 2 dòng</div>
<span style="font-family: "courier new" , "courier" , monospace;">compileOnly project(":modules:database:database-api")</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">compileOnly project(":modules:database:database-service")</span><br />
<div style="text-align: justify;">
vào file<i> build.gradle </i>của module <i>database-web</i> và <i>refresh </i>lại project là được (click chuột phải vào project <i>database-web</i> > <i>Gradle</i> --> <i>Refresh Gradle Project</i>).<br />
<h2>
Chạy!</h2>
<div>
Sau khi hoàn thành 02 module trên, ta sẽ tiến hành triển khai module vào cổng thông tin và kéo các module vào trang để hiện thị danh sách các bản ghi có trong CSDL (xem cách deploy và kéo portlet vào trang ở bài <a href="http://chingovan.blogspot.com/2017/04/liferay-7-tao-mot-portlet-moi-nhu-nao.html">Liferay 7: Tạo một portlet mới như thế nào?</a>).</div>
<div>
Ban đầu, bảng Student trong CSDL chưa có bản ghi nào, portlet sẽ hiển thị dòng thông báo là "Nothing!". Bạn thêm vài bản ghi vào bảng Student (thêm thủ công) và deploy lại module database-service và refresh lại trình duyệt. Lúc này, portlet sẽ hiển thị tất cả các bản ghi trong bảng Student lên trình duyệt dưới dạng bảng như hình:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-xpNQBTWrywQ/WUgIt23HAqI/AAAAAAAACag/vlJwd0NKVhAykkHVb_wGIDMC5yc9LZMoACLcBGAs/s1600/student-list.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="134" data-original-width="520" height="102" src="https://3.bp.blogspot.com/-xpNQBTWrywQ/WUgIt23HAqI/AAAAAAAACag/vlJwd0NKVhAykkHVb_wGIDMC5yc9LZMoACLcBGAs/s400/student-list.png" width="400" /></a></div>
</div>
<h2>
Kết luận</h2>
<div>
Đây là một hướng dẫn đơn giản cho người mới bắt đầu làm quen với Liferay 7. Trong bài này, mình đã trình bày thêm khai niện mới là Liferay Workspace và cách tổ chức các project theo phong cách của Liferay Workspace.</div>
<div>
Để sử dụng source code của mình, bạn chỉ cần tạo một LW trên IDE của bạn (Eclipse hoặc Liferay Studio). Sau đó, clone source code của mình tại <a href="https://github.com/programmerit/database" target="_blank">đây</a>. Từ IDE, bạn import các project của mình vào LW của bạn hoặc sao chép tất cả các file vào thư mục modules của LW rồi refresh lại LW là được.</div>
<div>
Bài viết này khá dài, mình cũng đã review cẩn thận nhưng cũng không thể tránh khỏi các thiếu sót. Mong các bạn cho ý kiến đóng góp hoặc sửa đổi.</div>
</div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com2tag:blogger.com,1999:blog-324075707410296810.post-70311555575016203562017-06-01T00:59:00.002+07:002020-07-18T01:27:15.276+07:00JasperReport: Giới thiệu về JasperReport<a href="https://3.bp.blogspot.com/-hiTS8pWrv7E/WS8Df4h0H1I/AAAAAAAACX8/1F7kR99mVukbtaUyRjyebtD4hFrj56iiQCLcB/s1600/cover.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="150" data-original-width="200" src="https://3.bp.blogspot.com/-hiTS8pWrv7E/WS8Df4h0H1I/AAAAAAAACX8/1F7kR99mVukbtaUyRjyebtD4hFrj56iiQCLcB/s1600/cover.png" /></a><br />
<div style="text-align: justify;">
JasperReport là một engine tạo báo cáo mã nguồn mở phổ biế nhất hiện tại. Nó được viết hoàn toàn bằng Java và được cho phép sử dụng với nhiều loại nguồn dữ liệu (data source) và tạo ra những báo cáo "chuẩn từng pixcel" cho phép hiển thị, in ấn hoặc xuất ra những định dạng như HTML, PDF, MS Excel, OpenOffice và MS Word.</div>
<div style="text-align: justify;">
Hầu hết các ứng dụng trong thực tế cần phải xuất các báo cáo theo nghiệp vụ cũng như theo yêu cầu của khách hàng. Một công cụ tạo báo cáo đơn giản, mạnh mẽ sẽ giúp bạn tiết kiệm được nhiều công sức, thời gian. Vì lý do trên, mình quyết định làm một seri các bài giới thiệu và các tip của bản thân để giới thiệu đến các bạn.</div>
<a name='more'></a><h2>
Các thành phần</h2>
<div>
<ul>
<li style="text-align: justify;">JDK phiên bản 1.3 trở lên. Vì JasperReport được viết hoàn toàn bằng Java nên cần phải cài đặt JDK thì mới sử dụng được thư viện này.</li>
<li style="text-align: justify;">jasperreports-x.x.x.jar: đây là file thư viện được xây dựng sẵn (trong các ví dụ sau này mình sử dụng phiên bản <a class="name" href="https://sourceforge.net/projects/jasperreports/files/jasperreports/JasperReports%206.3.1/jasperreports-6.3.1.jar/download" style="-webkit-tap-highlight-color: rgb(0, 119, 170); background: rgb(250, 250, 250); box-sizing: inherit; color: #0477aa; font-family: sans-serif; font-size: 13px; margin: 0px; outline: none; padding: 0px; text-decoration-line: none; vertical-align: baseline; white-space: nowrap;" title="Click to download jasperreports-6.3.1.jar">jasperreports-6.3.1.jar</a>).</li>
<li style="text-align: justify;">iReport: công cụ để sinh ra các file template (bạn có thể tải về các bản phù hợp với hệ điều hành tại <a href="http://community.jaspersoft.com/project/ireport-designer/releases" target="_blank">đây</a>, hoặc tài bản cài đặt cho HĐH Windows tại <a href="http://community.jaspersoft.com/modal_forms/nojs/jf-user-login-register?optoutdest=https://sourceforge.net/projects/ireport/files/iReport/iReport-5.6.0/iReport-5.6.0-windows-installer.exe/download" target="_blank">địa chỉ</a>).</li>
</ul>
</div>
<h2>
Cài đặt</h2>
<div>
<div style="text-align: justify;">
Việc cài đặt các thành phần trên đã rất đơn giản.</div>
</div>
<div>
<ul>
<li style="text-align: justify;">Cài đặt JDK, bạn tải bản JDK mới nhất về cài đặt vào máy.</li>
<li style="text-align: justify;">jasperreport-x.x.x.jar đây là file thư viện, bạn chỉ cần copy vào project là được hoặc nếu build bằng Maven bạn có thể thêm dependency ở địa chỉ <a href="https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports/6.3.1" target="_blank">này</a>.</li>
<li style="text-align: justify;">Cài đặt iReport: tùy thuộc vào HĐH mà bạn cài đặt phiên bản phù hợp.</li>
</ul>
</div>
<h2>
Tạo báo cáo đầu tiên</h2>
<div>
<div style="text-align: justify;">
Trong hướng dẫn đầu tiên này, mình sẽ làm một ví dụ đơn giản và hướng dẫn các bạn sử dụng thư viện jasperreport-x.x.x.jar là chính. Ứng dụng iReport được sử dụng để tạo một mẫu báo cáo đơn giản chỉ chứa các label (static text), những thành phần phức tạp hơn sẽ được giới thiệu chi tiết trong các bài viết sau này.</div>
</div>
<h3>
Tạo project</h3>
<div>
<div style="text-align: justify;">
Trong hướng dẫn này mình sử dụng IDE Eclipse và công cụ Maven để build ứng dụng và quản lý thư viện. Mình sẽ tạo project với tên là first-jasper-report (tạo project với maven trên eclipse tại <a href="http://o7planning.org/vi/10131/huong-dan-su-dung-maven-cho-nguoi-moi-bat-dau" target="_blank">đây</a>).</div>
</div>
<div>
<div style="text-align: justify;">
Trong thư mục first-jasper-report/src/main, tạo thư mục resource/report-templates. Thư mục này sẽ chứa các mẫu báo cáo phục vụ cho việc sinh các báo cáo sau này.</div>
<h3>
Tạo mẫu báo cáo</h3>
<div style="text-align: justify;">
Trước hết, ta sẽ đi tạo một mẫu báo cáo đơn giản bằng ứng dụng iReport. Sau khi cài đặt ứng dụng, bạn hãy mở ứng dụng lên và tạo một mẫu báo cáo mới. Việc tạo mới một mẫu báo cáo rất đơn giản, bạn chỉ cần chọn File >> New. Một cửa sổ hiện lên, bạn chọn mẫu báo cáo như trong hình dưới:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-a3VH_hPhkB4/WSr1swgHkxI/AAAAAAAACXU/Le5Oxw9d4qkpNBpjqmIg6eQXmLVMIkhFwCLcB/s1600/report-templage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="551" data-original-width="728" height="302" src="https://1.bp.blogspot.com/-a3VH_hPhkB4/WSr1swgHkxI/AAAAAAAACXU/Le5Oxw9d4qkpNBpjqmIg6eQXmLVMIkhFwCLcB/s400/report-templage.png" width="400" /></a></div>
<div style="text-align: justify;">
Nhấn vào "Open this Template" để chuyển sang bước tiếp theo như hình dưới:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-hERK_DGh-Ns/WSr2JlHUTmI/AAAAAAAACXY/jT2qPVAyx_I8meqFAn3L5ppQe3J_UQDugCLcB/s1600/report-template-name.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="269" data-original-width="616" height="173" src="https://2.bp.blogspot.com/-hERK_DGh-Ns/WSr2JlHUTmI/AAAAAAAACXY/jT2qPVAyx_I8meqFAn3L5ppQe3J_UQDugCLcB/s400/report-template-name.png" width="400" /></a></div>
<div style="text-align: justify;">
Ở cửa số này, bạn nhập tên cho báo cáo và chọn thư mục lưu trữ. Cụ thể, mình sẽ lưu trữ mẫu báo cáo này vào thư mục resource/report-templates đã tạo ở bước trên trong project.</div>
<div style="text-align: justify;">
Nhấn Next để chuyển sang bước tiếp theo và Finish để kết thúc việc tạo mẫu báo cáo.</div>
<div style="text-align: justify;">
Lúc này, giao diện làm việc (editor) với mẫu báo cáo vừa tạo đã hiện ra cho phép bạn kéo và thả các component mong muốn vào mẫu báo cáo. Để đơn giản, mình kéo vào mẫu báo cáo 03 static text (label). Bạn có thể xem mẫu báo cáo mà mình đã tạo tại <a href="https://github.com/programmerit/liferay-tutorial/blob/master/first-jasper-report/src/main/resources/report-templates/first-report.jrxml" target="_blank">đây</a>.</div>
<h3>
Biên dịch và xuất báo cáo</h3>
</div>
<div>
<div style="text-align: justify;">
Tiếp theo, chúng ta sẽ tực hiện biên dịch mẫu báo cáo và thực báo cáo dạng <i>pdf </i>và <i>html</i>. Trước tiên, bạn hay xem đoạn code sau đây:</div>
</div>
<div>
<script src="https://gist.github.com/programmerit/8e80b227a47f61fc0644cb874e9259c3.js"></script><br /></div>
<div>
<div style="text-align: justify;">
Đoạn code trên thực hiện 03 công việc gồm: biên dịch mẫu báo cáo, truyền dữ liệu và xuất báo cáo. Mình sẽ đi giải thích chi tiết từng dòng code và nhiệm vụ của nó.</div>
<h4>
Biên dịch</h4>
</div>
<div>
<div style="text-align: justify;">
Bạn sử dụng iReport để tạo ra file mẫu báo cáo *.<i>jrxml</i> và file này sẽ được biên dịch thành file *.<i>jasper</i>. Nếu có sẵn file *.<i>jasper </i>thì bạn sẽ không cần thực hiện bước biên dịch này nữa mà tải trực tiếp mẫu báo cáo từ file *.<i>jasper</i>.</div>
</div>
<div>
<div style="text-align: justify;">
Bạn hãy để ý dòng 24 và 25, mình tạo đối tượng JasperDesign từ file *.<i>jrxml </i>và biên dịch nó thành đối tượng <i>JasperReport</i>. Thực tế, đối tượng JasperReport cũng có thể được tạo từ file *.<i>jasper </i>(mình sẽ trình bày ở bài viết khác).</div>
</div>
<h4>
Truyền dữ liệu</h4>
<div>
<div style="text-align: justify;">
Sau khi biên dịch thành công, bạn cần truyền dữ liệu vào để sinh báo cáo. Dữ liệu ở đây có thể từ nhiều nguồn khác nhau và nhiều loại khác nhau. Mình sẽ trình bày cụ thể từng loại.</div>
</div>
<div>
<div style="text-align: justify;">
Dòng 27 thực hiện truyền dữ liệu vào là một <i>Map </i>rỗng, vì mẫu báo cáo của mình là mẫu báo cáo đơn giản nên hiện tại bước này mình sẽ không đi chi tiết.</div>
</div>
<h4>
Xuất báo cáo</h4>
<div>
<div style="text-align: justify;">
Dòng 29 và 31 được sử dụng để xuất báo cáo thành file pdf và html tương ứng. Đây là ví dụ đơn giản nên mình sử dụng tiện ích <i>JasperExportManager </i>để xuất báo cáo. Những ứng dụng nâng cao hơn hoặc xuất các loại dữ liệu đặc biệt thì mình sẽ trình bày ở bài sau.</div>
</div>
<h2>
Chạy ứng dụng</h2>
<div>
<div style="text-align: justify;">
Mình sử dụng maven là công cụ thể build project ví dụ. Trước hết bạn cần Update Project để tài về các gói thư viện cần thiết.</div>
</div>
<div>
<div style="text-align: justify;">
Tiếp theo bạn đóng gói ứng đụng và chạy ứng dụng từ lớp <i>App</i>.</div>
</div>
<div>
<div style="text-align: justify;">
Bạn sẽ thấy 02 file báo cáo là <i>first-report.pdf</i> và <i>first-report.html</i> được tạo ra trong ổ <i>D</i>. Mở lần lượt các file này nên bạn sẽ thấy nội dung của báo cáo giống với nội dung trong mẫu báo cáo.</div>
</div>
<h2>
Kết luận</h2>
<div>
<div style="text-align: justify;">
Đây là bài mở đầu cho series hướng dẫn sử dụng thư viện jasper để sinh báo cáo trong các ứng dụng Java. Bài hướng dẫn này khá đơn giản, chỉ hướng dẫn các bước rất đơn giản để tạo một báo cáo cũng rất đơn giản. Các kỹ thuật nâng cao sẽ được trình bày trong các bài viết tiếp theo.</div>
</div>
<div>
<div style="text-align: justify;">
Bạn có thể tải về mã nguồn tại <a href="https://github.com/programmerit/liferay-tutorial/tree/master/first-jasper-report" target="_blank">đây</a>.</div>
</div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-46152463129762772692017-05-08T23:28:00.002+07:002020-07-18T01:27:48.134+07:00Liferay 7: How to create a new portlet?<div style="text-align: justify;">
<a href="https://3.bp.blogspot.com/-owONuLtaGCM/WQIv6FZW3JI/AAAAAAAACT0/-o2qotuAVOgMnCI8eSSOo3uJcHtVPv9kACLcB/s1600/Liferay7-new%2Bportlet%2Bcopy.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://3.bp.blogspot.com/-owONuLtaGCM/WQIv6FZW3JI/AAAAAAAACT0/-o2qotuAVOgMnCI8eSSOo3uJcHtVPv9kACLcB/s1600/Liferay7-new%2Bportlet%2Bcopy.png" /></a><br />
There were many interesting features added or improved in Liferay 7. And Liferay Workspace is one of them, it is a new way to organizing our source code and recommended by Liferay. In this post, I will create a portlet by Liferay 7 default style ( it means using Liferay Workspace).<br />
(<a href="http://chingovan.blogspot.com/2017/04/liferay-7-tao-mot-portlet-moi-nhu-nao.html" target="_blank">Phiên bản Tiếng Việt - Vietnamese version</a>)</div>
<a name='more'></a><br />
<h2>
Environment installation</h2>
<h3>
Liferay 7's tools</h3>
<div>
Firstly, let's download Liferay 7 Portal and install the development environment. And take a look the different between Liferay 7 and previous. Here are the tools and its links:</div>
<div>
<ul>
<li style="text-align: justify;">Liferay Portal 7.0.2 GA3: Click <a href="https://sourceforge.net/projects/lportal/files/Liferay%20Portal/7.0.2%20GA3/liferay-ce-portal-tomcat-7.0-ga3-20160804222206210.zip/download" target="_blank">here</a> to download.</li>
<li style="text-align: justify;">Liferay IDE 3.1.0 M1: Let's download the suit version (<a href="https://sourceforge.net/projects/lportal/files/Liferay%20IDE/3.1.0%20M1/liferay-ide-eclipse-windows-x64-3.1.0-m1-201612060357.zip/download" target="_blank">Windows x64</a>, <a href="https://sourceforge.net/projects/lportal/files/Liferay%20IDE/3.1.0%20M1/liferay-ide-eclipse-linux-x64-3.1.0-m1-201612060357.tar.gz/download" target="_blank">Linux x64</a> or other versions at <a href="https://sourceforge.net/projects/lportal/files/Liferay%20IDE/3.1.0%20M1/" target="_blank">here</a>). Mote: you can download only Liferay IDE and then plug in your Eclipse IDE. But, using the Liferay IDE Eclipse which is provided by Liferay is the best.</li>
</ul>
<h3>
Configurations</h3>
<div>
Unzip two downloaded files and change some configurations.<br />
<h4>
Database configuration</h4>
</div>
</div>
<div>
<div style="text-align: justify;">
The database configuration is similar to Liferay 6.x's one (<a href="https://web.liferay.com/community/wiki/-/wiki/Main/Database+Portal+Properties" target="_blank">there is the best tutorial</a>). You have to create a file named portal-ext.properties in <i>{liferay-portal-home}/tomcat-8.0.32/webapps/ROOT/WEB-INF/classes/ </i>folder. And you must provide some configuration like this:</div>
<script src="https://gist.github.com/programmerit/e05f5e6f6869ab9ccbf4874cf7281d71.js"></script><br />
<div style="text-align: justify;">
I am using PostgreSQL database in my example, I created a database name lportal7 in the local machine. If your database is different with mine, let's see the tutorial in this <a href="https://dev.liferay.com/discover/reference/-/knowledge_base/7-0/database-templates" target="_blank">link</a>. Note, Liferay 7 CE version in only supported the open source database like MySQL, PostgreSQL, ... (click <a href="https://web.liferay.com/web/jamie.sammons/blog/-/blogs/liferay-portal-7-0-ce-release" target="_blank">here</a> to reach the details).</div>
</div>
<div>
<h4>
Eclipse and Liferay portal configuration.</h4>
<div style="text-align: justify;">
Create new Liferay Server: Open Eclipse IDE which has been unzipping. On the menu, click on New > Server > Server link below figure:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-kVbGcNG0urs/WP-MFGRjOLI/AAAAAAAACSE/NB92F5DBOjUNyQfz8td1wscd0ZluTwR_QCLcB/s1600/new%2Bserver.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="309" src="https://4.bp.blogspot.com/-kVbGcNG0urs/WP-MFGRjOLI/AAAAAAAACSE/NB92F5DBOjUNyQfz8td1wscd0ZluTwR_QCLcB/s320/new%2Bserver.png" width="320" /></a></div>
<br />
<div style="text-align: justify;">
Click on Next button and select Liferay, Inc. > Liferay 7.x, see the next figure:</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-2miop_ePmMk/WP-ObeKfRFI/AAAAAAAACSY/l-jSvaI06fEYhqrPaBmub3BDtxkUvb-3QCLcB/s1600/new%2Bliferay%2Bserver.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://3.bp.blogspot.com/-2miop_ePmMk/WP-ObeKfRFI/AAAAAAAACSY/l-jSvaI06fEYhqrPaBmub3BDtxkUvb-3QCLcB/s320/new%2Bliferay%2Bserver.png" width="285" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
Click on Next button to move the next step:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-WAraUJ7w4jc/WP-Oxv1zmfI/AAAAAAAACSo/PdzZRdMQLDErsyl3mac9JIabPzzvpeClQCLcB/s1600/config%2Bliferay%2Bportal.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="262" src="https://2.bp.blogspot.com/-WAraUJ7w4jc/WP-Oxv1zmfI/AAAAAAAACSo/PdzZRdMQLDErsyl3mac9JIabPzzvpeClQCLcB/s320/config%2Bliferay%2Bportal.png" width="320" /></a></div>
<div style="text-align: justify;">
At this step, enter the name of the server in name text box and browse to the Liferay Portal Bundle folder (which have been unzipping). Next, you must select the suit JRE runtime version (JRE 1.8 version is used in this example). Then, click on Finish button to get the end of configuration.<br />
Last, you have to turn the created server on. After starting up, let's open a browser and go the http://localhost:8080 page. Amazing, your portal is here, you got it!</div>
<ul></ul>
</div>
<h2>
Create new portlet</h2>
<div>
After the setting up, as a Liferay developer, I will create a new project by Liferay 7 style. So, how do I create it?<br />
On the menu of Eclipse IDE, click on File > New > Liferay Module Project. A new popup window appears and you have to fill some information about your project like the below figure:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-N22M0j-F4qg/WQIjjHqMMBI/AAAAAAAACTE/Ds10EluFv1QRvqtexNMNYpLpqoFVJMo2wCLcB/s1600/new%2Bliferay%2Bportlet.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="307" src="https://1.bp.blogspot.com/-N22M0j-F4qg/WQIjjHqMMBI/AAAAAAAACTE/Ds10EluFv1QRvqtexNMNYpLpqoFVJMo2wCLcB/s320/new%2Bliferay%2Bportlet.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
You have to enter the project name, select a build tool and project template.There were two build tools (gradle and maven). You must select a fit project template (there were many project templates, I will introduce about them in the future posts.). For the purpose of introduction, I have choose mv-portlet template and click on Next button to reach the next step.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-cGgCXyfzNqg/WQIkhecSV5I/AAAAAAAACTM/Wq21F0VFO-0GM95NYHZyNpH-8GBtbJCXACLcB/s1600/new%2Bliferay%2Bportlet%2B1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="226" src="https://4.bp.blogspot.com/-cGgCXyfzNqg/WQIkhecSV5I/AAAAAAAACTM/Wq21F0VFO-0GM95NYHZyNpH-8GBtbJCXACLcB/s320/new%2Bliferay%2Bportlet%2B1.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
In step, you have to enter the main class and package's name. And, lest click on Finish button to finish this creation. Now, you created a new project and the below figure is its structure.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-SRF6T66qckY/WQIlbqnxVFI/AAAAAAAACTU/AiqSawBtJaAL-3PYqiETWmQ3OksiQJU4ACLcB/s1600/liferay%2Bportlet%2Bstructure.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://3.bp.blogspot.com/-SRF6T66qckY/WQIlbqnxVFI/AAAAAAAACTU/AiqSawBtJaAL-3PYqiETWmQ3OksiQJU4ACLcB/s400/liferay%2Bportlet%2Bstructure.png" width="227" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
The structure of project which creates by gradle or maven tool is same. Java code and the resources separated. It is easy to code management</div>
<div class="separator" style="clear: both; text-align: justify;">
Let's take a look, you do not see some familiar file like portlet.xml, liferay-portlet.xml và liferay-display.xml. Where are they?</div>
<div class="separator" style="clear: both; text-align: justify;">
Next, we will review the content of TestPortlet class,</div>
<script src="https://gist.github.com/programmerit/e862aeeaae7ea719c75a1405ba7ac1e8.js"></script><br />
<div class="separator" style="clear: both; text-align: justify;">
As you can see, this class contains a new part (line 12th to 24th). Take a closer look, they are the pairs of key and value which were defined in some configuration file like portlet.xml, liferay-portlet.xml và liferay-display.xml. So, Liferay 7 instead of writing them on configuration files, write into Java code.</div>
<h2 style="clear: both; text-align: justify;">
Portlet deployment</h2>
<div class="separator" style="clear: both; text-align: justify;">
Next step is the deployment created project into the server. It is the easy task. You only right click on Liferay 7x server and chose "Add or Remove...." in float menu. Let's choose the portlet which will be deployed and click on OK button. The deployment will be complete automatically.</div>
<div class="separator" style="clear: both; text-align: justify;">
Open your browser and drag your portlet on your page. Let's click on "+" button on the top right browser:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-L5fchmjjzzE/WQIqYxT3r4I/AAAAAAAACTk/fI6G9aPLTcojU8dFanSCOilG-gzG-wJMwCLcB/s1600/add%2Bapplication%2Bto%2Bpage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="134" src="https://2.bp.blogspot.com/-L5fchmjjzzE/WQIqYxT3r4I/AAAAAAAACTk/fI6G9aPLTcojU8dFanSCOilG-gzG-wJMwCLcB/s320/add%2Bapplication%2Bto%2Bpage.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
Opening the "Sample" category and drag your portlet into any position. And then, your portlet will be displayed.</div>
<h2 style="clear: both; text-align: justify;">
Conclusion</h2>
<div class="separator" style="clear: both; text-align: justify;">
There is a simple Liferay 7 tutorial, it is for the Liferay beginner. And you can get my example project at <a href="https://github.com/programmerit/liferay-tutorial/tree/master/test-portlet" target="_blank">here</a>.</div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-79921588774410221972017-05-06T01:52:00.003+07:002020-07-18T01:28:35.659+07:00Liferay 7: Tạo mới một portlet phiên bản 6x<div style="text-align: justify;">
<a href="https://4.bp.blogspot.com/-6yAHsjU6hLQ/WQzJ2FPcblI/AAAAAAAACVc/_QpK8q6bTkENFF37Kg0_F4lng2URN0d1gCLcB/s1600/create%2B6x%2Bportle%2Bthumbnail%2Bcopy.png" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://4.bp.blogspot.com/-6yAHsjU6hLQ/WQzJ2FPcblI/AAAAAAAACVc/_QpK8q6bTkENFF37Kg0_F4lng2URN0d1gCLcB/s1600/create%2B6x%2Bportle%2Bthumbnail%2Bcopy.png" /></a>Trong bài viết <a href="http://chingovan.blogspot.com/2017/04/liferay-7-tao-mot-portlet-moi-nhu-nao.html" target="_blank">này</a>, mình đã có giới thiệu cách tạo một portlet theo cách mới của phiên bản Liferay 7.0. Ở đó, mình dùng công cụ Gradle để build project. Nếu để ý một chút, bạn sẽ thấy rằng ở phiên bản 7.0 không thấy sự xuất hiện Liferay SDK. Vậy nếu tạo portlet phiên bản 6x cũ trên môi trường Liferay 7.0 thì như thế nào? Bài viết này sẽ trả lời câu hỏi trên.</div>
<a name='more'></a><br />
<h2>
Cài đặt môi trường</h2>
<div>
<div style="text-align: justify;">
Thực tế thì phiên bản Liferay 7.0 vẫn có Liferay SDK để phát triển các portlet, theme, hook,... của phiên bản trước và bạn có thể tải về Liferay SDK (trong bài viết này tôi sử dụng Lifeary SDK phiên bản 7.0 GA3) tại <a href="https://sourceforge.net/projects/lportal/files/Liferay%20Portal/7.0.2%20GA3/com.liferay.portal.plugins.sdk-7.0-ga3-20160804222206210.zip/download" target="_blank">đây</a>.</div>
</div>
<div>
Tiếp theo, giải nén file nén ta được thư mục "com.liferay.portal.plugins.sdk-7.0". Sao chép thư mục này vào cùng với thư mục chứa Liferay Portal và đổi tên thành sdk.</div>
<div>
Mở file build.properties trong thư mục sdk, bạn cần sửa đổi một số nội dung sau:</div>
<div>
<ol>
<li style="text-align: justify;">Sửa dòng app.server.parent.dir=${sdk.dir}/../bundles thành app.server.parent.dir=C:/Users/ChiNV/Development/liferay/portal ở đây C:/Users/ChiNV/Development/liferay/portal là đường dẫn đến thư mục Liferay Portal.</li>
<li style="text-align: justify;">Thêm dòng app.server.dir=${app.server.parent.dir}/tomcat-8.0.32, lưu ý tomcat-8.0.32 là tên của thư mục chứa Tomcat nằm trong thư mục C:/Users/ChiNV/Development/liferay/portal (bạn phải điền đúng tên thư mục)</li>
</ol>
<div style="text-align: justify;">
Với các phiên bản trước ta có thể cấu hình Liferay SDK trên eclipse một cách dễ dàng. Phiên bản Liferay 7.0 hơi khác một chút và ta sẽ thực hiện việc cấu hình trong quá trìn tạo ra portlet, theme hoặc hook,... mới.</div>
</div>
<h2>
Tạo mới portlet</h2>
<h3>
Tạo mới portlet plugin project</h3>
<div>
Việc tạo mới một portlet 6x thức sự rất đơn giản. Trên tool Eclipse, bạn chọn File --> New --> Liferay Plugin Portlet (Liferay 6.x) như hình:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-N7Job0xnWx0/WQTeyfFzn2I/AAAAAAAACUQ/ybxEJkm5mj0RIW6XwFFU_P4ZWGucKbbnQCLcB/s1600/add%2B6x%2Bportlet.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="133" src="https://4.bp.blogspot.com/-N7Job0xnWx0/WQTeyfFzn2I/AAAAAAAACUQ/ybxEJkm5mj0RIW6XwFFU_P4ZWGucKbbnQCLcB/s400/add%2B6x%2Bportlet.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
Bước tiếp theo chúng ta sẽ điền các thông tin của project như tên, kiểu build (mình dùng Ant), kiểu plugin như hình:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-FTuWImKCwWM/WQTfQTSU5XI/AAAAAAAACUU/GPvbD3akHJYMSwA2l24pUuwwN4qGSsJzgCLcB/s1600/create%2B6x%2Bportlet.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="285" src="https://2.bp.blogspot.com/-FTuWImKCwWM/WQTfQTSU5XI/AAAAAAAACUU/GPvbD3akHJYMSwA2l24pUuwwN4qGSsJzgCLcB/s320/create%2B6x%2Bportlet.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
Nhấn Next để chuyển sang bước tiếp theo như hình dưới:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-HSgj82bzgLQ/WQTgCcwQivI/AAAAAAAACUc/CALxgtSKpAcpF8gplVM5RjMqNLDMg9HDQCLcB/s1600/create%2B6x%2Bportlet-liferay-mvc.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://3.bp.blogspot.com/-HSgj82bzgLQ/WQTgCcwQivI/AAAAAAAACUc/CALxgtSKpAcpF8gplVM5RjMqNLDMg9HDQCLcB/s320/create%2B6x%2Bportlet-liferay-mvc.png" width="300" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
Mình chọn kiểu plugin là Liferay MVC để làm ví dụ. Bạn có thể nhập thêm các thông tin khác hoặc để mặc định. Nhấn nút Next để chuyển sang bước cài đặt Liferay SDK như hình:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-AA7iqpwIFa4/WQTgW66LZxI/AAAAAAAACUg/eCaXo7Rv7c03mXhoyOyRWXISrvqgr7FHwCLcB/s1600/liferay%2Bsdk%2Bconfiguration.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="152" src="https://3.bp.blogspot.com/-AA7iqpwIFa4/WQTgW66LZxI/AAAAAAAACUg/eCaXo7Rv7c03mXhoyOyRWXISrvqgr7FHwCLcB/s320/liferay%2Bsdk%2Bconfiguration.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
Tại bước này, bạn cần trỏ đường dẫn đến đúng vị trí của thư mục sdk và nhấn nút Finish để kết thúc việc cài đặt. Nếu là lần đầu tiên tạo một projet theo kiểu 6.x thì chắc chắn bạn sẽ phải đợi lâu đấy.</div>
<h3 style="clear: both; text-align: justify;">
Tạo mới Liferay portlet</h3>
<div style="text-align: justify;">
Ở trên, ta đã tạo xong một portlet plugin project. Bước tiếp theo ta sẽ đi tạo mới một liferay portlet. Việc tạo mới một liferay portlet đối với phiên bản Liferay 7.0 cũng không khác gì với các phiên bản trước. Trên giao diện Eclipse, bạn chọn File --> New --> Liferay Portlet. Một cửa sổ xuất hiện và ta điền các thông tin cần thiết như sau:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-75DqbHy1myg/WQTjC1VB0WI/AAAAAAAACUs/v_omPTSMyVkKRRJ6_olrQlnCGlt83W50wCLcB/s1600/create%2B6x%2Bportlet%2Bplugin.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="294" src="https://1.bp.blogspot.com/-75DqbHy1myg/WQTjC1VB0WI/AAAAAAAACUs/v_omPTSMyVkKRRJ6_olrQlnCGlt83W50wCLcB/s320/create%2B6x%2Bportlet%2Bplugin.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
Đầu tiên, ta cần chọn portlet plugin project để biên portlet này sẽ thêm vào project nào. Bước tiếp là chọn source folder (cái này theo mình thì nên để mặc định). Tiếp theo chọn tên và package cho lớp Controller và cuối cùng là chọn lớp cha cho lớp controller (để đơn giản chon ví dụ mình chọn MVCPortlet như trong hình).</div>
<div class="separator" style="clear: both; text-align: justify;">
Bước tiêp theo là điều thông tin cho portlet, trong trường hợp này mình để mặc định:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-QWWSRLNpqvM/WQTjoEBGwxI/AAAAAAAACU0/fN5se3eAtmswRWLE1_Yz4e8HITOenatQACLcB/s1600/create%2B6x%2Bportlet%2Bplugin%2Bconfig%2B1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://2.bp.blogspot.com/-QWWSRLNpqvM/WQTjoEBGwxI/AAAAAAAACU0/fN5se3eAtmswRWLE1_Yz4e8HITOenatQACLcB/s320/create%2B6x%2Bportlet%2Bplugin%2Bconfig%2B1.png" width="312" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
Các thông tin khác:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-RS47A__L-W0/WQTj1_i4YuI/AAAAAAAACU4/6EnCyes8_Z09T1ucWn3Wy6keRYXIImbhQCLcB/s1600/create%2B6x%2Bportlet%2Bplugin%2Bconfig%2B2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://3.bp.blogspot.com/-RS47A__L-W0/WQTj1_i4YuI/AAAAAAAACU4/6EnCyes8_Z09T1ucWn3Wy6keRYXIImbhQCLcB/s320/create%2B6x%2Bportlet%2Bplugin%2Bconfig%2B2.png" width="304" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
Cuối cùng, nhấn vào nút Finish để kết thúc việc thêm mới một portlet.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-owlGl08cfrY/WQTkD0tV57I/AAAAAAAACU8/RoCOnLxlYGwC-JHj-aBqhM3DK7pfbHKlgCLcB/s1600/6x%2Bportlet%2Bstructure.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://2.bp.blogspot.com/-owlGl08cfrY/WQTkD0tV57I/AAAAAAAACU8/RoCOnLxlYGwC-JHj-aBqhM3DK7pfbHKlgCLcB/s320/6x%2Bportlet%2Bstructure.png" width="151" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
Lúc này, bạn sẽ có một project có cấu trúc như hình trên, giống như các project của phiên bản trước đúng không nào?</div>
<h2 style="clear: both; text-align: justify;">
Triển khai</h2>
<div style="text-align: justify;">
Việc triển khai portlet vừa tạo lên máy chủ ở phiên bản Liferay 7.0 không có gì phức tạp (bạn có thể xem ở <a href="https://sourceforge.net/projects/lportal/files/Liferay%20Portal/7.0.2%20GA3/com.liferay.portal.plugins.sdk-7.0-ga3-20160804222206210.zip/download" target="_blank">đây</a>).</div>
<h2>
Kết luận</h2>
<div style="text-align: justify;">
Bàn viết này giới thiệu cho bạn các phát triển các portlet, theme, hook,... của phiên bản trước (6.x) trên phiên bản 7.0. Thực tế, việc này không có gì phức tạp, quan trọng nhất là bạn cấu hình sao cho Eclipse nhận ra được thư mục Liferay SDK, còn lại, việc phát triển portlet, theme hay hook là hoàn toàn tương tự.</div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-10472865854129358852017-04-28T00:53:00.002+07:002020-07-18T01:29:35.858+07:00Liferay 7: Tạo một portlet mới như thế nào?<div style="text-align: justify;"><a href="https://3.bp.blogspot.com/-owONuLtaGCM/WQIv6FZW3JI/AAAAAAAACT0/-o2qotuAVOgMnCI8eSSOo3uJcHtVPv9kACLcB/s1600/Liferay7-new%2Bportlet%2Bcopy.png" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://3.bp.blogspot.com/-owONuLtaGCM/WQIv6FZW3JI/AAAAAAAACT0/-o2qotuAVOgMnCI8eSSOo3uJcHtVPv9kACLcB/s1600/Liferay7-new%2Bportlet%2Bcopy.png" /></a><br />
<a href="http://chingovan.blogspot.com/2017/05/liferay-7-how-to-create-new-portlet.html" target="_blank">English version</a><br />
Ở bài viết <a href="http://chingovan.blogspot.com/2016/12/liferay-bat-au-voi-liferay-7.html" target="_blank">trước</a>, mình đã lược dịch lại những tính năng mà Liferay 7 cải tiến và bổ sung. Trong đó, Liferay Workspace là một tính năng mới, một cách tổ chức code mới được Liferay khuyến khích lập trình viên sử dụng. Trong bài viết này, mình sẽ tạo một portlet theo cấu hình mặc định của Liferay (tức là sử dụng Liferay Workspace), bài viết tiếp theo mình sẽ hướng dẫn cách cấu hình Liferay 7 để bạn có thể tạo ra các portlet theo phong cách cũ (phiên bản Liferay 6).<br />
</div><a name='more'></a>
<h2>Cài đặt môi trường</h2><h3>Tải các thành phần cần thiết</h3><div>Trước hết, ta hãy tải Liferay 7 và cài đặt môi trường lập trình, hãy xem phiên bản 7 có khác gì không nhé. Các thành phần cần tải về và liên kết như sau:</div><div><ul><li style="text-align: justify;">Liferay Portal 7.0.2 GA3: Bạn có thể tải về tại <a href="https://sourceforge.net/projects/lportal/files/Liferay%20Portal/7.0.2%20GA3/liferay-ce-portal-tomcat-7.0-ga3-20160804222206210.zip/download" target="_blank">đây</a>.</li>
<li style="text-align: justify;">Liferay IDE 3.1.0 M1: Bản có thể tải về tại các địa chỉ (<a href="https://sourceforge.net/projects/lportal/files/Liferay%20IDE/3.1.0%20M1/liferay-ide-eclipse-windows-x64-3.1.0-m1-201612060357.zip/download" target="_blank">Windows x64</a>, <a href="https://sourceforge.net/projects/lportal/files/Liferay%20IDE/3.1.0%20M1/liferay-ide-eclipse-linux-x64-3.1.0-m1-201612060357.tar.gz/download" target="_blank">Linux x64</a> hoặc các phiên bản khác tại <a href="https://sourceforge.net/projects/lportal/files/Liferay%20IDE/3.1.0%20M1/" target="_blank">đây</a>). Lưu ý, bạn có thể tải riêng Liferay IDE và tích hợp vào bản Eclipse IDE của mình, nhưng nếu bạn mới làm quen với Liferay thì tốt nhất tải bản tích hợp (Liferay IDE + Eclipse IDE) để đỡ mất thời gian cấu hình.</li>
</ul><h3>Cấu hình</h3><div>Giải nén hai tệp tin thu được ở bước trên và thực hiện một số cấu hình cần thiết.<br />
<h4>Cấu hình kết nối CSDL</h4></div></div><div><div style="text-align: justify;">Cấu hình kết nối cơ sở dữ liệu: phần này được thực hiện tương tự với phiên bản 6.2 (bạn có thể xem tại <a href="http://chingovan.blogspot.com/2015/05/cai-dat-va-cau-hinh-liferay.html" target="_blank">đây</a>). Bạn tạo file <i><b>portal-ext.properties </b></i>trong thư mục <i>{liferay-portal-home}/tomcat-8.0.32/webapps/ROOT/WEB-INF/classes/</i>. Với nội dung như sau:</div><script src="https://gist.github.com/programmerit/e05f5e6f6869ab9ccbf4874cf7281d71.js"></script><br />
<div style="text-align: justify;">Ở đây mình sử dụng cơ sở dữ liệu PostgreSQL, mình đã tạo sẵn một cơ sở dữ liệu tên là <i><b>lportal7 </b></i>trên máy local. Nếu bạn sử dụng các cơ sở dữ liệu khác thì có thể tham khảo ở <a href="https://dev.liferay.com/discover/reference/-/knowledge_base/7-0/database-templates" target="_blank">đây</a>.</div>Lưu ý, Liferay 7 phiên bản CE chỉ hỗ trợ các CSDL mã nguồn mở như MySQL, Postgress, ... (chi tiết tại <a href="https://web.liferay.com/web/jamie.sammons/blog/-/blogs/liferay-portal-7-0-ce-release" target="_blank">đây</a>)</div><div><h4>Cấu hình eclipse và portal</h4><div style="text-align: justify;">Tạo Liferay Server: Mở IDE Eclipse vừa giải nén ở trên. Bạn chọn New > chọn Server / Server như hình:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://4.bp.blogspot.com/-kVbGcNG0urs/WP-MFGRjOLI/AAAAAAAACSE/NB92F5DBOjUNyQfz8td1wscd0ZluTwR_QCLcB/s1600/new%2Bserver.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="309" src="https://4.bp.blogspot.com/-kVbGcNG0urs/WP-MFGRjOLI/AAAAAAAACSE/NB92F5DBOjUNyQfz8td1wscd0ZluTwR_QCLcB/s320/new%2Bserver.png" width="320" /></a></div><br />
<div style="text-align: justify;">Bạn nhấn vào nút Next và chọn Liferay, Inc. và chọn Liferay 7.x như hình dưới:</div><div class="separator" style="clear: both; text-align: center;"></div><br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://3.bp.blogspot.com/-2miop_ePmMk/WP-ObeKfRFI/AAAAAAAACSY/l-jSvaI06fEYhqrPaBmub3BDtxkUvb-3QCLcB/s1600/new%2Bliferay%2Bserver.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://3.bp.blogspot.com/-2miop_ePmMk/WP-ObeKfRFI/AAAAAAAACSY/l-jSvaI06fEYhqrPaBmub3BDtxkUvb-3QCLcB/s320/new%2Bliferay%2Bserver.png" width="285" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"></div>Nhấn vào nút Next để chuyển sang bước tiếp theo<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://2.bp.blogspot.com/-WAraUJ7w4jc/WP-Oxv1zmfI/AAAAAAAACSo/PdzZRdMQLDErsyl3mac9JIabPzzvpeClQCLcB/s1600/config%2Bliferay%2Bportal.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="262" src="https://2.bp.blogspot.com/-WAraUJ7w4jc/WP-Oxv1zmfI/AAAAAAAACSo/PdzZRdMQLDErsyl3mac9JIabPzzvpeClQCLcB/s320/config%2Bliferay%2Bportal.png" width="320" /></a></div><div style="text-align: justify;">Tại bước này, bạn đặt tên cho Server ở ô "Name" và chọn đường dẫn đến thư mục portal vừa giải nén ở bước trên. Tiếp theo chọn JRE runtime cho phù hợp (phiên bản 1.8) và nhấn Finish để kết thúc việc cài đặt.</div><div style="text-align: justify;">Bước cuối cùng là bạn bật (start) server vừa tạo. Sau khi khởi động xong, bạn mở trình duyệt web và truy cập vào địa chỉ http://localhost:8080</div><ul></ul></div><h2>Tạo portlet mới</h2><div>Sau khi hoàn thành các bước cài đặt trên, với vai trò là một lập trình viên, mình sẽ tạo một project đầu tiên theo phong cách của Liferay 7.</div><div>Để tạo mới một portlet, trên giao diện IDE Eclipse, chọn File --> New --> Liferay Module Project. Một cửa sổ hiện ra như sau:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-N22M0j-F4qg/WQIjjHqMMBI/AAAAAAAACTE/Ds10EluFv1QRvqtexNMNYpLpqoFVJMo2wCLcB/s1600/new%2Bliferay%2Bportlet.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="307" src="https://1.bp.blogspot.com/-N22M0j-F4qg/WQIjjHqMMBI/AAAAAAAACTE/Ds10EluFv1QRvqtexNMNYpLpqoFVJMo2wCLcB/s320/new%2Bliferay%2Bportlet.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: justify;">Bạn cần nhập tên của project, chọn công cụ build và mẫu project. Công cụ build project có 2 loại là gradle và maven, bạn có thể chọn công cụ tuỳ thích. Mẫu project có nhiều loại, tuỳ thuộc vào từng dự án mà bạn chọn loại phù hợp (mình sẽ giới thiêu chi tiết ở các bài khác), để cho đơn giản mình chọn mẫu mvc-portlet và nhấn nùt Next để chuyển sang bước tiếp.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://4.bp.blogspot.com/-cGgCXyfzNqg/WQIkhecSV5I/AAAAAAAACTM/Wq21F0VFO-0GM95NYHZyNpH-8GBtbJCXACLcB/s1600/new%2Bliferay%2Bportlet%2B1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="226" src="https://4.bp.blogspot.com/-cGgCXyfzNqg/WQIkhecSV5I/AAAAAAAACTM/Wq21F0VFO-0GM95NYHZyNpH-8GBtbJCXACLcB/s320/new%2Bliferay%2Bportlet%2B1.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: justify;"><br />
</div><div class="separator" style="clear: both; text-align: justify;">Ở bước này, bạn cần đặt tên cho lớp xử lý chính và tên gói. Ngoài ra, bạn có thể cung cấp thêm các thuộc tính (các thuộc tính này sẽ được giới thiệu sau). Nhấn Finish để kết thúc việc tạo mới một portlet.</div><div class="separator" style="clear: both; text-align: justify;">Portlet được tạo và bạn sẽ thấy cấu trúc của nó như hình dưới:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://3.bp.blogspot.com/-SRF6T66qckY/WQIlbqnxVFI/AAAAAAAACTU/AiqSawBtJaAL-3PYqiETWmQ3OksiQJU4ACLcB/s1600/liferay%2Bportlet%2Bstructure.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://3.bp.blogspot.com/-SRF6T66qckY/WQIlbqnxVFI/AAAAAAAACTU/AiqSawBtJaAL-3PYqiETWmQ3OksiQJU4ACLcB/s400/liferay%2Bportlet%2Bstructure.png" width="227" /></a></div><div class="separator" style="clear: both; text-align: justify;">Cấu trúc của portlet sử dụng công cụ build Gradle và Maven cũng khá tương tự nhau. Phân java code và resource tách biệt nhau nên sẽ dễ dàng hơn cho việc quản lý code.</div><div class="separator" style="clear: both; text-align: justify;">Nhìn chi tiết hơn một chút, chúng ta không thấy các file cấu hình portlet như portlet.xml, liferay-portlet.xml và liferay-display.xml, phải chăng đây là sự lược giảm của Liferay 7 so với các phiên bản trước?</div><div class="separator" style="clear: both; text-align: justify;">Trước hết, hãy xem nội dung của lớp TestPortlet,</div><script src="https://gist.github.com/programmerit/e862aeeaae7ea719c75a1405ba7ac1e8.js"></script><br />
<div class="separator" style="clear: both; text-align: justify;">So với phiên bản trước, ta có thấy một phần mới đó là phần chú thích @Component (dòng 12 đến 24). Nếu để ý kỹ hơn, ta có thể thấy các cặp khoá và giá trị của phần property chính là các cấu hình trong các file portlet.xml, liferay-portlet.xml và liferay-display.xml. Như vậy, Liferay 7 thay vì sử dụng các file cấu hình thì chuyển trực tiếp các cấu hình vào trong java code.</div><h2 style="clear: both; text-align: justify;">Triển khai portlet</h2><div class="separator" style="clear: both; text-align: justify;">Tiếp theo là việc triển khai portlet vừa tạo lên máy chủ. Trong giai đoạn phát triển thì việc này hết sức đơn giản. Bạn chỉ cần click chuột phải vào máy chủ Liferay 7.x vừa tạo ở bước trên. Chọn "Add or Remove..." và chọn portlet muốn triển khai là được. Portlet của bạn sẽ được triển khai lên máy chủ một cách tự động. </div><div class="separator" style="clear: both; text-align: justify;">Truy cập vào ứng dụng trên trình duyêt và thực hiện kéo portlet của bạn vào trang. Bạn nhấn vào nút "+" ở góc trên bên phải như hình:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://2.bp.blogspot.com/-L5fchmjjzzE/WQIqYxT3r4I/AAAAAAAACTk/fI6G9aPLTcojU8dFanSCOilG-gzG-wJMwCLcB/s1600/add%2Bapplication%2Bto%2Bpage.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="134" src="https://2.bp.blogspot.com/-L5fchmjjzzE/WQIqYxT3r4I/AAAAAAAACTk/fI6G9aPLTcojU8dFanSCOilG-gzG-wJMwCLcB/s320/add%2Bapplication%2Bto%2Bpage.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: justify;">Bạn chọn mục "Sample" và kéo portlet vào vị trí theo ý bạn. Lúc này, nội dung của portlet sẽ hiện lên trên màn hình.</div><h2 style="clear: both; text-align: justify;">Kết luận</h2><div class="separator" style="clear: both; text-align: justify;">Đây là bài viết đơn giải giới thiệu về Liferay 7 cho người mới làm quen với Liferay. Bạn có thể tại project ví dụ tại <a href="https://github.com/programmerit/liferay-tutorial/tree/master/test-portlet" target="_blank">đây</a>.</div>ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-6827507173672674882017-03-15T12:58:00.001+07:002020-07-18T01:29:47.253+07:00Java: Tham chiếu và tham trị<div style="text-align: justify;">Java truyền tham số vào phương thức (hàm) theo kiểu tham số hay tham trị? Một câu hỏi rất dễ tìm câu trả lời trên Google. Và câu trả lời tất nhiên là tham trị! Nhưng vẫn có rất nhiều junior đã, đang nhầm lẫn và cảm thấy nghi ngờ. Bài viết này sẽ đưa ra và ví dụ minh họa để cho ai vẫn đang nhầm lẫn có một cái nhìn rõ hơn.</div><a name='more'></a>
<h2>Java truyền tham số theo tham trị!</h2><div><div style="text-align: justify;">Để kiểm chứng lại kết quả trên Google có đúng là Java truyền tham số vào phương thức theo kiểu tham trị hay không. Ta sẽ làm một ví dụ rất đơn giản, đó là phương thức đổi giá trị của hai biến (<i>swap</i>).</div></div><div><script src="https://gist.github.com/programmerit/c13d66b44cccc891ac5d8d6830956a6c.js"></script><br />
</div><div><div style="text-align: justify;">Hãy dịch và chạy thử file <i><b>SwapInt.java</b></i>. Bạn thấy gì nào?</div><div style="text-align: justify;">Giá trị trước khi truyền vào phương thức swap là <i>c = 1</i> và <i>d = 2</i>. Sau khi truyền vào phương thức <i>swap </i>thì giá trị của nó cũng không thay đổi.</div><div style="text-align: justify;">Hoặc ví dụ với trường hợp tham số truyền vào là object (không phải kiểu nguyên thủy) như sau:</div></div><div><script src="https://gist.github.com/programmerit/c19cb97b310c3bdc63f99292d63a9184.js"></script></div><div><div style="text-align: justify;">Hãy dịch và chạy đoạn code trên đi (bạn cần thêm lớp <i><b><a href="https://gist.github.com/programmerit/946c8025b49cd5f4da0a37e7a96be07a" target="_blank">Point</a></b></i>), kết quả thế nào? Phương thức <i>swap </i>vẫn không đổi giá trị cho nhau đúng không?</div></div><div><div style="text-align: justify;">Vậy hóa ra Google nói đúng, Java truyền tham số vào phương thức theo kiểu tham trị. Câu chuyện rõ ràng như vậy, sao vẫn còn nhiều người nhầm lẫn và nghi ngờ?</div></div><h2>Trường hợp nhầm lẫn</h2><div><div style="text-align: justify;">Hãy xem ví dụ sau đây (cái mà một số junior vẫn hay nhầm nhất):</div></div><div><script src="https://gist.github.com/programmerit/34ac154e0476b2059a526ed291d717a1.js"></script></div><div><div style="text-align: justify;">Bạn hãy dịch, chạy file <b><i>SwapPointRef.java </i></b>đi và cho tôi biết kết quả. Kết quả thế nào? Rõ ràng là khác 2 lần trước đúng không? Nội dung in ra màn hình đã có sự xáo trộn rồi, có vẻ là chúng đổi chỗ cho nhau thật.</div><div style="text-align: justify;">Khoan đã, ta sẽ xem lại phương thức <i>swap </i>của lớp <b><i>SwapPointRef </i></b>với <b><i>SwapPoint </i></b>và <i><b>SwapInt </b></i>xem có gì khác nhau không.</div></div><div><script src="https://gist.github.com/programmerit/a30103e834dd5cd9dc90af47c3644301.js"></script><br />
</div><div><div style="text-align: justify;">Mình gom 3 phương thức <i>swap </i>lại gần nhau cho dễ nhìn. Bạn đã nhìn thấy sự khác biệt chưa?</div><div style="text-align: justify;">Nếu bạn vẫn chưa nhận thấy sự khác biệt thì mình sẽ chỉ cho bạn thấy ngay. Đối với phương thức <i>swap</i> của lớp <i><b>SwapPoint </b></i>và <i><b>SwapInt</b></i>, mình đổi chỗ <i><b>hai tham số truyền vào</b></i> còn với phương thức <i>swap </i>ở lớp <i><b>SwapPointRef </b></i>mình chỉ "<i>đổi chỗ</i>" giá trị của <i><b>hai trường của hai tham số</b></i>.</div><div style="text-align: justify;">À đúng rồi nhỉ? Nhưng tôi tin bạn vẫn còn một thắc mắc đó là: <i>Nếu như Java truyền tham số theo kiểu tham trị (tức là truyền giá trị) thì các tham số được truyền vào là bảo sao (copy) của dữ liệu. Việc thay đổi trên dữ liệu copy đó thì sao lại ảnh hưởng để nội dung của dữ liệu gốc được</i>?</div><div style="text-align: justify;">Để trả lời câu hỏi trên, bạn hãy quan sát hình vẽ dưới đây:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://3.bp.blogspot.com/-l6LRUOOmPco/WMjVo8WKZsI/AAAAAAAACQ8/wlCRKaSf1BckhDiqLdf3pG4M_IurROkJgCLcB/s1600/Java%2Bref.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://3.bp.blogspot.com/-l6LRUOOmPco/WMjVo8WKZsI/AAAAAAAACQ8/wlCRKaSf1BckhDiqLdf3pG4M_IurROkJgCLcB/s200/Java%2Bref.png" width="185" /></a></div><div style="text-align: justify;"></div><ul><li>Hình chữ nhật thể hiện cho mối đối tượng của lớp (trong hình là một đối tượng của lớp Point).</li>
<li>Mỗi hình tròn thể hiện cho một "tham chiếu" để đối tượng (có thể hiểu đây là những cái tên của một người).</li>
</ul><br />
Để rõ hơn bạn hãy xem ví dụ sau:</div><script src="https://gist.github.com/programmerit/6486777167f2f2bb407be6a380101c16.js"></script><br />
<div><div style="text-align: justify;">Chạy ví dụ này và bạn thấy rằng, dù bạn thay đổi một nội dung của bất cứ "cái tên nào" thì tất cả nội dung của các "cái tên" khác cũng đều ảnh hưởng theo (hiểu đơn giản như sau: ở nhà gọi là cu Tèo, đến lớp gọi là Nam, đi làm gọi là Bắc, nếu Bắc trúng xổ số thành tỷ phú thì cu Tèo hay thằng Nam cũng là tỷ phú.).</div><div style="text-align: justify;">Như vậy, việc đổi chỗ trong hàm <i>swap </i>của lớp <i><b>SwapInt </b></i>và <i><b>SwapPoint </b></i>thực chất chỉ là đổi chỗ hai "cái tên". Những cái tên sẽ được tạo ra khi ta gọi phương thức và mất đi khi phương thức kết thúc. Tuy nhiên, tại một thời điểm cụ thể, "mỗi cái tên" sẽ ám chỉ một đối tượng cụ thể (hoặc là không nếu ta gán tên đó với giá trị <i>null</i>) nên khi tác động vào nội dung của mỗi cái tên là ta sẽ tác động vào nội dung của đối tượng.</div><i>Do đó, một lưu ý quan trọng là khi bạn truyền một đối tượng vào hàm thì hãy cẩn thận, rất có thể nội dung của đối tượng đã bị thay đổi khi gọi hàm.</i><br />
<h2>Kết luận</h2></div><div><div style="text-align: justify;">Java truyền tham số theo kiểu tham biến hay tham trị là một câu hỏi rất dễ tìm câu trả lời. Tuy nhiên, với junior thì không phải cái nào cũng dễ tìm. Hi vọng bài viết giúp các junior hiểu sâu sắc hơn vấn đề này.</div></div>ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-63494244810268751762016-12-31T01:10:00.001+07:002017-02-06T10:53:49.833+07:00Liferay 7: Bắt đầu với Liferay 7<div style="text-align: justify;">
<a href="https://2.bp.blogspot.com/-5Cv_fCdX2yg/WGai33IzoSI/AAAAAAAACNo/R_Dd-vkXjOktxUzpCiKOsxtC8oZ1Q0p9ACLcB/s1600/liferay%2B7.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://2.bp.blogspot.com/-5Cv_fCdX2yg/WGai33IzoSI/AAAAAAAACNo/R_Dd-vkXjOktxUzpCiKOsxtC8oZ1Q0p9ACLcB/s1600/liferay%2B7.png" /></a>Mỗi lần nâng cấp phiên bản, Liferay đều có những cái rất mới mẻ và có thêm những cải tiến hay. Lần này cũng thế, phiên bản 7 với nhiều tính năng mới được trang bị và khác xa với phiên bản 6. Chúng ta hãy cùng đi cài đặt Liferay 7 và xem những tính năng mới của nó dưới con mắt của một developer nhé.</div>
<a name='more'></a><div style="text-align: justify;">
Nền tảng Liferay 7 đã được dựng lại, làm cho Liferay trở lên dễ xây dựng và bảo trì hơn và cung cấp nhiều tính năng mới cho những người bắt đầu với Liferay hơn những phiên bản trước. Và sau đây là một vài tính năng chính được phát triển trong phiên bản này dành cho lập trình viên:</div>
<ol>
<li>Đơn giản và dễ học (đồ open source không bao giờ đơn giản và dễ học - ghi chú của tác giả)</li>
<li>Mô hình phát triển module hóa</li>
<li>Tăng cường khả năng dùng lại</li>
<li>Mở rộng hơn và dễ dàng trong việc bảo trì</li>
<li>Tối ưu hóa các công cụ phát triển</li>
<li>Khả năng cấu hình mạnh mẽ</li>
</ol>
<div>
Hãy cùng xem các tính năng này cụ thể như thế nào nhé</div>
<h2>
Đơn giản và dễ học</h2>
<div>
<div style="text-align: justify;">
Liferay đã luôn luôn đơn giản và dễ học (không tin) so với các sản phẩm thương mại tương tự; Trong phiên bản này, khoảng cách về tính đơn giản và dễ học so với các sản phẩm thương mại còn thậm chí rộng hơn nữa.</div>
</div>
<div>
<div style="text-align: justify;">
Liferay 7 đơn giản hơn các phiên bản trước đó là do một kết trúc module hóa và hợp lý. Hơn nữa, có rất nhiều cách đặc biệt để tạo ra các ứng dụng và mở rộng đã được tiến hóa theo các tiêu chuẩn chính thức và thực tế. Kết quả là, bây giờ lập trình viên có thể dễ sử dụng lại các kiến thức của họ và sử dụng những kiến thực họ học được bên ngoài Liferay.</div>
</div>
<div>
<div style="text-align: justify;">
Liferay 7 cũng dễ học, core đã được module hóa của nó cho phép lập trình viên và quản trị hệ thống gỡ bỏ những phần họ không cần hoặc không muốn; tính năng này sẽ làm giảm thời gian khởi động và bộ nhớ và kết quả là hiệu quả và hiệu năng được cải thiện.</div>
</div>
<h2>
Mô hình phát triển module hóa</h2>
<div>
<div style="text-align: justify;">
Nếu bạn đã từng sử dụng Liferay, bạn đã có kinh nghiệp với một số lợi ích của việc phát triển module hóa nhờ vào các plugin. Liferay 7 mang lợi ích này lên một tầm cao hoàn toàn mới.</div>
</div>
<div>
<div style="text-align: justify;">
Ngoài việc xây dựng các plugin như bạn đã làm ở các phiên bản trước, bạn có thể tận dụng ưu điểm của việc phát triển module hoàn toàn và chạy trên hệ thống dựa trên tiêu chuẩn OSGi. Liferay 7 cũng tạo điều kiện cho việc tạo ra các ứng dụng của tất cả các loại bằng cách tổng hợp và sử dụng lại các module.</div>
</div>
<div>
<div style="text-align: justify;">
Và đừng lo lắng, các module là hoàn toàn dễ hiểu. Một module là một bản phân phối giống như một file JAR và có thể nhỏ như một lớp Java hoặc lớn như một ứng dụng mà bạn nghĩ. Một ứng dụng trong Liferay có thể gồm có một module hoặc nhiêu module như bạn muốn. Điều thú vị là các module có thể cộng tác, cho phép bạn xây dựng các ứng dụng bằng các tổng hợp các phần nhỏ mà dễ dàng phát triển, triển khai, bảo trì và sử dụng lại.</div>
</div>
<h2>
Tăng cường khả năng dùng lại</h2>
<div>
<div style="text-align: justify;">
Nếu bạn đã làm việc trên các ứng dụng lớn phát triển trên Liferay, bạn đã có kinh nghiệp với tình huống trong đó bạn muốn chia sẻ một tập con của các lớp từ một plugin với các plugin khác.</div>
</div>
<div>
<div style="text-align: justify;">
Java EE không hỗ trợ bất kỳ cách tiêu chuẩn nào để làm được điều này, nhưng Liferay đã cung cấp cho bạn một khả năng nhất định để làm được điều này, với một cơ chế được biết đến là CLP mà ở đó nó sử dụng một bộ nạp để cho phép các plugin gọi các dịch vụ của các plugin khác được tạo ra trong Service Builder. Tuy nhiên, cơ chế này vẫn có một chút giới hạn (bộ nạp lớn của Java EE không cho phép nhiều hơn) và không cho phép bạn tự do xác định một lớp bất kỳ hoặc tất cả các lớp từ một module để sử dụng bên trong một module khác.</div>
</div>
<div>
<div style="text-align: justify;">
Liferay 7 cho phép tái sử dụng nhiều hơn, cả trong mã và bộ nhớ trong lúc chạy. Đối với bất kỳ chức năng nào được mong muốn tái sử dụng, bạn có thể tạo một module (hãy nhớ, nó chỉ là một file JAR với một số siêu dữ liệu) với các lớp mà bạn muốn và triển khai nó. Các module khác chỉ cần khai báo rằng chúng sử dụng các lớp trong module đó (bằng cách chỉ rõ gói của nó) và Liferay 7 tự động buộc chúng lại với nhau. Tất cả các lệnh gọi là theo cách gọi của Java thông thường. Hãy thử xem!</div>
</div>
<div>
<div style="text-align: justify;">
Cơ chế này giúp bạn tránh được những vấn đề khai báo JAR/classpath. Bạn sẽ không còn phải khai báo file JAR trong classpath và cũng không phải thực hiện tải các lớp phức tạp. Môi trường runtime sử dụng không gian lớp riêng biệt trên mỗi module và thậm chi có thể dùng các phiên bản thư viện khác nhau trong cùng một ứng dụng.</div>
</div>
<h2>
Mở rộng hơn và dễ bảo trì hơn</h2>
<div>
<div style="text-align: justify;">
Bất cứ khi nào bạn hỏi các lập trình viên Liferay về đặc điểm yêu thích nhất của họ đối với nền tảng Liferay là gì thì "Khả năng mở rộng tuyệt vời" là một trong top 03 câu trả lời phổ biến (không tin được - tác giả). Bạn có thể tùy biến hầu hết mọi chi tiết và thêm các chức năng của bạn vào đó.</div>
</div>
<div>
<div style="text-align: justify;">
Liferay 7 có mở rộng hơn không? Nhiều điểm mở rộng đã được thêm vào. Nhưng không chỉ có vậy, tất cả các điểm mở rộng mới và đã có (mà đã được cập nhật), sử dụng cơ chế mở rộng với dựa trên mo hình dịch vụ vụ OSGi. Sau đây là một vài lợi ích của cơ chế này:</div>
</div>
<div>
<ol>
<li style="text-align: justify;"><b>Đơn giản</b>: Việc cài đặt một điểm mở rộng bây giờ luôn là một lớp Java mà cài đặt một giao diện và có một annotation (@Component). Đó là nó đấy, không thế nào dễ hơn được nữa.</li>
<li style="text-align: justify;"><b>Dễ dàng để bảo trì</b>: Các điểm mở rộng bây giờ là được định nghĩa chặt chẽ thông qua một giao diện Java mà sử dụng luật phiên bản ngữ nghĩa (Semantic Versioning). Điều này có nghĩa rằng các mở rộng của bạn có thể làm việc mà không cần thay đổi thậm chí là trên vài phiên bản của Liferay miễn là các API mở rộng tương thích với phiên bản trước.</li>
<li style="text-align: justify;"><b>Năng động</b>: Các mở rộng có thể được tải và được gỡ bỏ bất cứ lúc nào trong quá trình phát triển hoặc trong khi chạy.</li>
</ol>
<div>
<div style="text-align: justify;">
Nhưng đó không phải là tất cả. Các ứng dụng của bạn bây giờ có thể tận dụng mô hình này và trở lên dễ mở rộng. Bạn có thể tạo ra các điểm mở rộng đơn gian chỉ bằng cách tạo ra một giao diện và chú thích một phương thức setter với một annotation (@Reference). Cài đặt tính mở rộng chưa bao giờ đơn giản hơn.</div>
<h2>
Tối ưu hóa các công cụ phát triển</h2>
</div>
</div>
<div>
Liferay 7 cho phép bạn sử dụng các công cụ mà bạn thích.</div>
<div>
<div style="text-align: justify;">
Nếu bạn không có công cụ phát triển yêu thích nào hoặc bạn muốn sử dụng công cụ mặc định của chúng tôi thì chúng tôi khuyên bạn sử dụng công cụ Liferay Workspace. Nó tổ chức dưới dạng cấu trúc thư mục và hệ thống được xây dựng dựa vào Gradle và Bnd. Liferay Workspace có thể được sử dụng độc lập thông qua dòng lệnh hoặc với Liferay IDE, cái mà được chạy trên Eclipse.</div>
</div>
<div>
<div>
<div style="text-align: justify;">
Và nếu bạn muốn tiếp tục sử dụng Plugin SDK, chúng tôi cũng đã chuẩn bị sẵn cho bạn. Plugin SDK đã có sẵn (bạn có thể tải về trên trang chủ Liferay) để tạo điều kiện cho việc chuyển đổi lên Liferay 7 của bạn. Trong thực tế, tổ chức thư mục của Plugin SDK có thể được tổ chức bên trong Liferay Workspace bên cạnh kiến trúc mới được sử dụng để xây dựng môi trường mới; bạn có thể chuyển đổi các project cũ và mới theo lộ trình của bạn.</div>
</div>
<div>
<div style="text-align: justify;">
Cuối cùng, chúng tôi cũng đã phát triển một công cụ nhẹ gọi là Blade CLI, công cụ này sẽ thuận tiện cho việc bắt đầu những dự án mới từ các mẫu dự án - nó thì đặc biệt hữu ích cho Gradle cái không chứa các nguyên mẫu của Maven. Blade CLI cũng sử dụng các lệnh để start/stop máy chủ, triển khai và vận hành các module.</div>
</div>
<h2>
Khả năng cấu hình mạnh mẽ</h2>
</div>
<div>
<div style="text-align: justify;">
Việc tạo ra các đoạn code có thể cấu hình là đơn giản với Liferay 7. Và các ứng dụng mà sử dụng API có thể cấu hình mới cho phép quản trị viên thay đổi các cấu hình một cách nhanh chóng, thông qua giao diện người dùng được sinh tự động được gọi là System Settings.</div>
</div>
<div>
<div style="text-align: justify;">
Bây giờ, bạn đã hiểu tại sao Liferay 7 làm giàu kinh nghiệm của bạn như một lập trình viên mà làm cho việc phát triển và cấu hình các ứng dụng trở lên đơn giản rồi chứ.</div>
</div>
<div>
<h2>
Kết luận</h2>
</div>
<div style="text-align: justify;">
Trên đây là bài dịch tóm tắt những tính năng và ưu điểm mới của Liferay 7. Trong những bài tiếp theo tôi sẽ đi vào chi tiết cài đặt và xem cụ thể những tính năng mới đó có đúng như Liferay giới thiệu hay không. Bạn có thể đọc bài viết gốc tại <a href="https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-0/benefits-of-liferay-7-for-liferay-6-developers" target="_blank">đây</a>.</div>
<div>
</div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com1tag:blogger.com,1999:blog-324075707410296810.post-21255067630595302022016-09-13T09:06:00.000+07:002016-09-13T09:06:03.648+07:00Liferay: Vòng đời của Portlet - Phương thức serveResource()<a href="https://3.bp.blogspot.com/-s7mbeGDTMHI/V9UiyFfm2JI/AAAAAAAACK4/KAbHJj-1dmYyhmrIHDs1KmfLqmtobWikQCLcB/s1600/serveResource%2Bmethod%2Bcopy.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://3.bp.blogspot.com/-s7mbeGDTMHI/V9UiyFfm2JI/AAAAAAAACK4/KAbHJj-1dmYyhmrIHDs1KmfLqmtobWikQCLcB/s1600/serveResource%2Bmethod%2Bcopy.png" /></a><br />
<div style="text-align: justify;">
Portlet có thể gửi các nội dung động từ máy trạm tới máy chủ trong pha phục vụ tài nguyên (serve resource). Chúng ta hãy xem pha này được thực hiện trong Liferay như thế nào nhé.</div>
<a name='more'></a><br />
<div style="text-align: justify;">
Các portlet được sử dụng để sinh ra các kết quả tổng hợp. Mỗi portlet sẽ sinh ra một phần nhỏ nội dung của một trang. Chúng ta đã biết rằng pha <i>render</i> được sử dụng để sinh ra nội dung của <i>portlet</i>. <i>render</i> và <i>action </i>là hai pha ban đầu được định nghĩa trong mô tả kỹ thuật phiên bản 1.0 (<i>JSR-168</i>).</div>
<div style="text-align: justify;">
Nhưng sau này, các portlet được mong đợi có thể phục vụ các nội dung khác hơn là chỉ có <i>HTML</i>. Ví dụ, gửi một luồng các byte trực tiếp đến máy trạm (tải file ảnh, <i>PDF</i>, <i>XLS</i>, <i>JSON</i>, <i>XML</i>, vv) hoặc gửi các đầu ra động mà không cần làm mới (<i>refresh</i>) lại trang web (thông qua <i>AJAX</i>). Mô tả kỹ thuật của <i>portlet</i> ở phiên bản 1.0 thì không cung cấp trực tiếp cách phục vụ các tài nguyên động như vậy.</div>
<div style="text-align: justify;">
Để khắc phục điều này, mô tả kỹ thuật của <i>portlet</i> đã được cập nhật (thông số kỹ thuật của <i>portlet</i> mới được gọi là phiên bản 2.0 hay <i>JSR-286</i>) chứa các chức năng được thêm vào này. Một trong những chức năng chính được thêm vào trong mô tả kỹ thuật phiên bản 2.0 (<i>JRS-286</i>) là khả năng phục vụ các tài nguyên.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-xVFNfzUSWUA/V9UipBeosgI/AAAAAAAACK0/lDc8oK6GQI4HhiyipsqC5ATwDpe2hQiaQCLcB/s1600/serve_resource_phase.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://4.bp.blogspot.com/-xVFNfzUSWUA/V9UipBeosgI/AAAAAAAACK0/lDc8oK6GQI4HhiyipsqC5ATwDpe2hQiaQCLcB/s1600/serve_resource_phase.png" /></a></div>
<div style="text-align: justify;">
Trước <i>JRS-286</i>, <i>servelet</i> bên trong <i>portlet </i>đã được sử dụng để phục vụ các nội dung động.</div>
<h2>
Pha phục vụ tài nguyên</h2>
<div style="text-align: justify;">
Trong pha phục vụ tài nguyên, <i>portlet</i> có thể gửi dữ liệu động mà không cần gọi pha <i>render</i>. Không giống như pha <i>action</i>, Liferay sẽ không gọi phương thức <i>render </i>sau khi hoàn thành pha phục vụ tài nguyên. Tài nguyên được yêu cầu sẽ được gửi trực tiếp tới máy trạm trong pha này.</div>
<h3>
Cài đặt pha phục vụ tài nguyên như thế nào?</h3>
<div style="text-align: justify;">
Phương thức <i><b>serveResource()</b></i> thể hiện cho pha phục vụ dữ liệu của <i>portlet</i>. Phương thức này được định nghĩa trong giao diện <i>ResourceServingPortlet</i>. Nếu chúng tôi nói về <i>Liferay MVC Portlet</i>, nó sẽ có sơ đồ phân cấp lớp như sau:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-yWGMlAKQ60c/V9UkCvfcV_I/AAAAAAAACLA/56ZaJ-wGEFkWywHObnt_jInC6wH41h8agCLcB/s1600/Portlet_Class_Heirarchy.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="357" src="https://1.bp.blogspot.com/-yWGMlAKQ60c/V9UkCvfcV_I/AAAAAAAACLA/56ZaJ-wGEFkWywHObnt_jInC6wH41h8agCLcB/s400/Portlet_Class_Heirarchy.png" width="400" /></a></div>
<div style="text-align: justify;">
<ul>
<li><b>GenericPortlet </b>cài đặt 04 giao diện sua:</li>
<ul>
<li><b>Portlet </b>- Giao diện này định nghĩa các phương thức <i>init</i>, <i>destroy</i>, <i>render</i> và <i>processAction</i>. Các phương thức này thể hiện cho các pha khởi tạo, <i>destroy</i>, <i>render</i> và <i>action </i>và nó được giới thiệu trong <i>JSR-168</i>.</li>
<li><b>EventPortlet </b>- Giao diện này định nghĩa phương thức <i>processEvent</i>. Phương thức này thể hiện cho pha <i>event </i>của <i>portlet</i> và được thêm vào trong <i>JSR-286</i> (phiên bản 2.0).</li>
<li><b>PortletConfig</b> - Giao diện này định nghĩa các phương thức khác nhàu để lấy trạng thái của <i>portlet </i>(<i>portlet context</i>) và đọc các tham số khởi tạo của <i>portlet</i>, lấy tên <i>portlet</i> và các tài nguyên khác, ...</li>
<li><b>ResourceServingPortlet </b>- Giao diện này định nghĩa phương thức <i><b>serveResource()</b></i>. Phương thức này thể hiện cho pha phục vụ dữ liệu của <i>portlet</i> và đã được thêm vào trong <i>JSR-286</i>.</li>
</ul>
<li><b>GenericPortlet </b>cung cấp cài đặt mặc định của các giao diện này.</li>
<li>Phương thức <b><i>serveResource() </i></b>được định nghĩa trong giao diện <i>ResourceServingPortlet</i>.</li>
<li><b>GenericPortlet</b> cung cấp cài đặt mặc định của phương thức <i><b>serveResource()</b></i>.</li>
<li><b>LiferayPortlet</b> kế thừa từ <i>GenericPortlet </i>và ghi đè phương thức <i><b>serveResource()</b></i>.</li>
<li><b>MVCPortlet </b>kết thừa từ <i>LiferayPortlet </i>và lại ghi đè phương thức <i><b>serveResource()</b></i>.</li>
</ul>
</div>
<div style="text-align: justify;">
Nếu bạn muốn cài đặt pha phục vụ dữ liệu, bạn cần phải ghi đè phương thức <i><b>serveResource() </b></i>trong lớp của bạn (trong trường hợp này là lớp <i>MVCPortlet</i>)</div>
<h3>
Phương thức serveResource() được gọi như thế nào?</h3>
<div style="text-align: justify;">
Phương thức <i><b>serveResource()</b></i> được gọi khi <i>resourceURL</i> của portlet được nhấn vào. <i>resourceURL </i>sẽ trỏ đến <i>portlet</i> mà đã tạo ra nó. Nó có thể trỏ tới cùng một <i>portlet </i>hoặc <i>portlet</i> khác dựa trên các tham số được đặt trong <i>resourceURL</i>.</div>
<h3>
Luồng thực hiện của phương thức serveResource </h3>
<div style="text-align: justify;">
Sơ đồ tuần tự bên dưới thể hiện luồng thực hiện của phương thức <i><b>serveResource()</b></i>.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-ffgVuiSh1sk/V9UnJRK7soI/AAAAAAAACLQ/pEr9pQKUwvIc28-DxWJcMNERXH9kBJ1ngCLcB/s1600/serveResource_sequence_flow.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="177" src="https://2.bp.blogspot.com/-ffgVuiSh1sk/V9UnJRK7soI/AAAAAAAACLQ/pEr9pQKUwvIc28-DxWJcMNERXH9kBJ1ngCLcB/s400/serveResource_sequence_flow.png" width="400" /></a></div>
<h3 style="clear: both; text-align: left;">
Luồng thực hiện</h3>
<div style="text-align: justify;">
<ul>
<li>Khi người dùng nhấn vào một <i>resourceURL</i>, nó sẽ truyền tới <i>Liferay Portal</i>.</li>
<li>Liferay sẽ tiếp nhận yêu cầu này và chuyển nó tới <i>portlet container</i>.</li>
<li><i>Portlet container</i> sẽ tìm portlet thích hợp để gọi phương thức <i><b>serveResource()</b></i> của nó.</li>
<li>Sau đó, <i>portlet container </i>gửi đầu ra của phương thức <i><b>serveResource() </b></i>tới <i>Liferay Portal</i> và cuối cùng <i>Liferay Portal</i> sẽ gửi các nội dung đó trực tiếp đến máy trạm.</li>
<li>Bạn có thể thấy rằng <i>portlet container </i>sẽ không thực hiện gọi bất kỳ pha nào khác sau khi hoàn thành pha phục vụ tài nguyên (sau khi kết thúc phương thức <i><b>serveResource()</b></i>).</li>
<li>Nó ít nhiều giống với một <i>servlet</i> cái mà sẽ gửi kết quả đầu tra đến máy trạm một cách trực tiếp.</li>
<li>Trong trường hợp của phương thức render, sau khi hoàn thành pha <i>render</i>, <i>Liferay Portal </i>sẽ tổng hợp các kết quả đầu ra của mọi <i>portlet </i>lên một trang và gửi tới máy trạm.</li>
<li>Nhưng trong trường hợp của pha phục vụ dữ liệu, pha <i>render </i>không được gọi bởi vậy <i>Liferay Portal </i>sẽ không sinh nội dung của các <i>portlet</i>. Nó đơn giả chỉ gửi đầu ra từ phương thức <i><b>serveResource() </b></i>tới máy trạm. Bởi vậy, bạn luôn luôn cần phải gọi <i>resourceURL </i>trong <i>AJAX</i>.</li>
</ul>
</div>
<h2>
Tổng kết</h2>
<div style="text-align: justify;">
<ul>
<li>Pha phục vụ tài nguyên được giới thiệu trong mô tả kỹ thuật của portlet 2.0 (<i>JSR-286</i>)</li>
<li>Trong pha phục vụ tài nguyên, portlet có thể gửi các nội dung động một cách trực tiếp đến máy trạm. Nó có thể là văn bản <i>HTML</i>, <i>JSON</i>,<i> XML </i>hoặc các luồng byte như gửi một file.</li>
<li>Phương thức <i><b>serveResource() </b></i>thể hiện trong pha phục vụ dữ liệu của <i>portlet</i>. Bạn cần phải ghi đè phương thức này và đặt các xử lý logic trong nó để phục vụ các nội dung động trực tiếp đến máy trạm.</li>
<li>Sau khi hoàn thành phương thức <i><b>serveResource()</b></i>, <i>Liferay Portal </i>sẽ gửi kết quả của phương thức <i><b>serveResource() </b></i>trực tiếp đến máy trạm. Nó sẽ không tạo ra toàn bộ nội dung của một trang. Do đó, bạn chỉ nên gọi phương thức <i><b>serveResource() </b></i>trong <i>AJAX</i>.</li>
<li>Bạn có thể đọc bài viết gốc tại đây: <a href="http://www.opensource-techblog.com/2015/06/portlet-serve-resource-phase-lifecycle.html">http://www.opensource-techblog.com/2015/06/portlet-serve-resource-phase-lifecycle.html</a></li>
</ul>
</div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com1tag:blogger.com,1999:blog-324075707410296810.post-58027532179141906922016-09-11T01:34:00.001+07:002016-09-11T16:05:33.680+07:00Liferay: Vòng đời của Portlet - Phương thức processAction()<div style="text-align: justify;">
<a href="https://3.bp.blogspot.com/-bgyHfmF0Sos/V9UeVtNWv6I/AAAAAAAACKc/Y3XlR_ykTIQZ1UfcpqmIZICTGOYFnOgbgCLcB/s1600/processAction%2Bcopy.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://3.bp.blogspot.com/-bgyHfmF0Sos/V9UeVtNWv6I/AAAAAAAACKc/Y3XlR_ykTIQZ1UfcpqmIZICTGOYFnOgbgCLcB/s1600/processAction%2Bcopy.png" /></a>Pha action (hoạt động) của portlet được thực hiện trong phương thức <i><b>processAction()</b></i>. Nó được thực hiện bằng cách gọi một URL action. Người dùng sẽ tương tác với portlet trong pha này.</div>
<a name='more'></a><br />
<div style="text-align: justify;">
Pha action được sử dụng để thực hiện một thoa tác nào đó trên một portlet. Chỉ duy nhất một portlet của một trang có thể thực hiện pha này trong một thời điểm. Ví dụ, nếu có 10 portlet được kéo ra trên cùng một trang, thì pha action có thể được gọi bởi một portlet tại một thời điểm mà thôi. Trong pha action, ta có thể thay đổi các trạng thái của portlet hoặc thực hiện các thao tác <i><b>CRUD</b></i> (<i>Create - Read - Update - Delete</i>).</div>
<div style="text-align: justify;">
Thông số kỹ thuật của portlet (JSR-168 và 286) đã định nghĩa pha render. Câu hỏi được đặt ra trong đầu là cái gì cần giới thiệu trong pha action của portlet? Ta sẽ đi trả lời câu hỏi này bằng cách thực hiện ví dụ dưới đây.</div>
<div style="text-align: justify;">
Trước khi giải thích thêm, hãy để tôi nhắc lại portlet được thiết kế để sinh ra các kết quả tổng hợp. Nói cách khác, nhiều hơn một portlet trên một trang có thể sinh ra toàn bộ nội dung của trang. (Mỗi portlet sẽ sinh ra một phần nhỏ nội dung của các trang).<a href="http://chingovan.blogspot.com/2016/08/liferay-phuong-thuc-render.html" rel="nofollow" target="_blank"> Pha render</a> được gọi khi trang chứa nó được làm mới hoặc renderURL của bất kỳ portlet nào trên trang đó được nhấn vào.</div>
<div style="text-align: justify;">
Bây giờ chúng ta sẽ thực hiện một ví dụ theo kịch bản sau đây:<br />
<ul>
<li>Hai portlet (<i>Portlet1</i> và <i>Portlet2</i>) được đặt trên cùng một trang.</li>
<li><i>Portlet1</i> đang thực hiện một thao tác <i><b>CRUD</b></i>. Giải sử rằng, không có bất kỳ pha action nào trong <i>Portlet1</i> và do đó, thao tác <i><b>CRUD</b></i> sẽ được đặt trong phương thức <i><b>render()</b></i>.</li>
<li>Khi người dùng nhấn vào <i>renderURL</i> của <i>Portlet1</i>, thao tác <i><b>CRUD</b></i> sẽ được thực hiện trong phương thức <i><b>render()</b></i>.</li>
</ul>
<div>
Vấn đề xuất hiện khi người dùng nhấn vào <i>renderURL</i> của <i>Portlet2</i>. Hãy để tôi giải thích tại sao?</div>
<div>
<ul>
<li>Nếu phương thức <i><b>render()</b></i> của bất kỳ portlet nào trên một trang được gọi thì phương thức <i><b>render()</b></i> của tất cả các portlet khác trên trang đó cũng sẽ được gọi.</li>
<li>Khi người dùng click vào <i>renderURL</i> của <i>Portlet2</i> thì phương thức <i><b>render()</b></i> của <i>Portlet1</i> cũng sẽ được gọi và do đó thao tác <i><b>CRUD</b></i> cũng sẽ được thực hiện một cách không cần thiết.</li>
<li>Người dùng đã không tương tác trực tiếp với <i>Portlet1</i> nhưng thao tác <i><b>CRUD</b></i> của <i>Portlet1</i> vẫn được thực hiện. Đây là việc cần phải tránh.</li>
<li>Tại sao điều này xảy ra? Bởi vì, portlet không biết về portlet khác sẽ được đặt trên cùng một trang trong quá trình phát triển.</li>
</ul>
</div>
<div style="text-align: justify;">
Thông số kỹ thuật của portlet đã giới thiệu pha action để tránh hạn chế này. Pha action của portlet có thể được gọi bởi <i>actionURL</i> và được gọi bởi một portlet tại một thời điểm trên một trang cho trước. Pha render của portlet thì sẽ được gọi ngay sau khi pha action hoàn thành.</div>
</div>
<div style="text-align: justify;">
Một thao tác lý tưởng không nên được lặp đi lặp lại và phải được định nghĩa trong pha action. Nói cách khác, pha action là nơi tốt nhất để viết bất kỳ đoạn mã nào mà không được lặp lại một cách vô ý (ví dụ các thao tác <i><b>CRUD</b></i> như chúng ta đã thấy ở trên).</div>
<h2>
Pha action của portlet</h2>
<div style="text-align: justify;">
<ul>
<li>Phương thức action của portlet thực hiện pha action.</li>
<li>Sơ đồ trình tự sau đây sẽ thể hiện việc làm thế nào pha <i>render</i> và <i>action</i> làm việc trong portlet.</li>
</ul>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-Om6uEVhC3i0/V9RKu9uQWoI/AAAAAAAACKA/WHdvlm_UzBkpSL5Iuw88R_COAVPpnCSqACLcB/s1600/Portlet-2BFlow.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="277" src="https://3.bp.blogspot.com/-Om6uEVhC3i0/V9RKu9uQWoI/AAAAAAAACKA/WHdvlm_UzBkpSL5Iuw88R_COAVPpnCSqACLcB/s400/Portlet-2BFlow.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div style="text-align: justify;">
<ul>
<li>Bước 1: Người dùng thực hiện một yêu cầu từ <i>Page1</i>. <i>Portlet-A</i> và <i>Portlet-B</i> được đặt trong trang <i>Page1</i> này.</li>
<li>Bước 2: Liferay sẽ thực hiện yêu cầu đó và truyền nó đến <i>portlet container</i>.</li>
<li>Bước 3: <i>Portlet container</i> sẽ gọi phương thức render của <i>Portlet-A</i>.</li>
<li>Bước 4: <i>Portlet container</i> sẽ nhận các kết của từ <i>Portlet-A</i>.</li>
<li>Bước 5: <i>Portlet container</i> sẽ gọi phương thức <i>render</i> của <i>Portlet-B</i>.</li>
<li>Bước 6: <i>Portlet container</i> sẽ nhận các kết của từ <i>Portlet-B</i>.</li>
<li>Bước 7: <i>Portlet container</i> sẽ tổng hợp các kết của từ <i>Portlet-A</i> và <i>Portlet-B</i>.</li>
<li>Bước 8: Liferay sẽ gửi lại các kết quả.</li>
</ul>
</div>
<div style="text-align: justify;">
Pha <i>render</i> sẽ theo sau pha action.
</div>
<div style="text-align: justify;">
<ul>
<li>Bước 9: Bây giờ người dùng sẽ thực hiện một thao tác <i><b>CRUD</b></i> trên <i>Portlet-A</i>.</li>
<li>Bước 10: Liferay sẽ gọi pha action của portlet.</li>
<li>Bước 11: <i>Portlet container</i> sẽ gọi phương thức <i>action</i> của <i>Portlet-A</i>.</li>
<li>Bước 12: Theo mô tả kỹ thuật của portlet, pha <i>render</i> sẽ được gọi ngay sau khi pha action hoàn thành. Trong bước này, <i>portlet container</i> sẽ gọi phương thức <i>render</i> của <i>Portlet-A</i>.</li>
<li>Bước 13: <i>Portlet container</i> sẽ lấy kết quả từ <i>Portlet-A</i>.</li>
<li>Bước 14: Theo thông số kỹ thuật của portlet, khi phương thức <i>render</i> được gọi từ bất kỳ portlet nào trong một trang thì tất cả các phương thức render của các portlet khác trên trang đó cũng được gọi. Trong bức này, <i>portlet container</i> sẽ gọi phương thức <i>render</i> của <i>Portlet-B</i>.</li>
<li>Bước 15: <i>Portlet container</i> sẽ lấy kết quả từ <i>Portlet-B</i>.</li>
<li>Bước 16: <i>Portlet container </i>sẽ tổng hợp các kết quả lấy từ <i>Portlet-A</i> và <i>Portlet-B</i> và gửi lại.</li>
<li>Bước 17: Kết quả cuối cùng sẽ được gửi lại cho người dùng.</li>
</ul>
</div>
<div style="text-align: justify;">
<i>actionURL</i> được sử dụng để gọi phương thức action. Bạn có thể xem ở bài viết sau đây để biết làm thế nào để tạo ra một <i>actionURL</i> và hoàn thành luồng như hình vẽ trên.</div>
<div style="text-align: justify;">
<ul>
<li><a href="http://www.opensource-techblog.com/2015/03/action-url-by-portlet-actionurl-tag.html" target="_blank">Tạo một actionURL bằng thẻ portlet (portlet:actionURL) trong JSP.</a></li>
<li><a href="http://www.opensource-techblog.com/2015/04/create-action-url-programatically.html">Tạo một actionURL bằng Java API trong lớp Portlet và JSP bằng việc viết code.</a></li>
<li><a href="http://www.opensource-techblog.com/2015/04/action-url-by-liferay-portletactionurl.html">Tạo một actionURL bằng thẻ Liferay (liferay-portlet:actionURL) trong JSP</a></li>
<li><a href="http://www.opensource-techblog.com/2015/04/action-url-by-liferay-portleturl-createactionurl.html">Tạo một actionURL trong Javascript bằng AUI</a></li>
</ul>
</div>
<div style="text-align: justify;">
Bạn có thể xem bài viết <a href="http://www.opensource-techblog.com/2015/03/portlet-action-method-invocation-in-liferay.html">how action method internally invoke</a><a href="http://www.opensource-techblog.com/2015/03/portlet-action-method-invocation-in-liferay.html">d for Liferay Portlet</a> để hiểu làm thế nào Liferay portal xử lý yêu cầu của phương thức action.
</div>
<h2>
Tổng kết </h2>
<div style="text-align: justify;">
<ul>
<li>Phương thức action của portlet thực hiện pha action của nó.</li>
<li>Bất kỳ đoạn mã hoặc xử lý logic nào mà không cần lặp lại phải được thực hiện trong pha action.</li>
<li>Một khi pha action của portlet hoàn thành, <i>portlet container</i> sẽ gọi phương thức render của chính portlet đó.</li>
<li><i>actionURL</i> được sử dụng để gọi phương thức action của portlet.</li>
<li>Bạn có thể đọc bài viết gốc tại địa chỉ: <a href="http://www.opensource-techblog.com/2015/03/portlet-action-phase.html">http://www.opensource-techblog.com/2015/03/portlet-action-phase.html</a></li>
</ul>
</div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-83764057243701057732016-08-18T09:36:00.001+07:002017-02-06T10:54:10.653+07:00Liferay 6.2: Vòng đời của Portlet - Phương thức render()<div style="text-align: justify;">
<a href="https://4.bp.blogspot.com/-XgSqdWH7gYk/V9P4qJzI-YI/AAAAAAAACJY/6HSY5tN7jIkq84qfAXEBxdKkc29N-tf3QCLcB/s1600/render%2Bmethod%2Bcopy.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://4.bp.blogspot.com/-XgSqdWH7gYk/V9P4qJzI-YI/AAAAAAAACJY/6HSY5tN7jIkq84qfAXEBxdKkc29N-tf3QCLcB/s1600/render%2Bmethod%2Bcopy.png" /></a>Phương thức vòng đời render chịu trách nhiệm cho việc sinh ra nội dung (được hiển thị trên trình duyệt) của portlet. Nó được gọi khi portlet được thực thi trong pha sinh nội dung.</div>
<a name='more'></a><br />
<div style="text-align: justify;">
Trong pha này, portlet sẽ sinh ra các nội dung dựa vào trạng thái hiện tại của nó (ví dụ như: Normal, Minimize, Maximize, ...). Phương thức / pha render của tất cả các portlet trên cùng một trang sẽ đượcc gọi bất cứ khi nào trang đó được refresh hoặc người dùng chuyển hướng đến renderURL của một portlet nhất định (kích hoạt phương thức render bởi Render URL) hoặc bất kỳ một portlet nào trong trang mà trang đó hoàn thành một pha hành động (action) hoặc một sự kiện.</div>
<div style="text-align: justify;">
Chúng ta sẽ thao tác trên cùng một Project Liferay (portlet) mà ta đã tạo ra trong bài trước (Liferay: Vòng đời của Portlet - Phương thức init()). Trong lớp controller của portlet, chúng ta sẽ ghi đè phương thức render(). Phương thức này được định nghĩa trong lớp GenericPortlet.</div>
<div style="text-align: justify;">
Phương thức ghi đè trong lớp PhaseAndLifecyclePortlet và đặt vài dòng ghi log như đoạn code dưới đây:<br />
<script src="https://gist.github.com/programmerit/61183b3d2e8c89b6d93ce58bd4a44e08.js"></script></div>
<h2>
Giải thích</h2>
<div style="text-align: justify;">
Phương thức render() được ghi đè từ lớp GenericPorrtlet</div>
<div style="text-align: justify;">
Trong phương thức này, ngs ta đặt các dòng log. Nó sẽ được hiển thị khi phương thức <b>render()</b> được gọi.</div>
<div style="text-align: justify;">
Cuối phương thức, ta thực gọi super.render(request, response). Nó sẽ gọi phương thức <b>render()</b> của lớp GenericPortlet để thực hiện vài thao tác xử lý ở phía back-end (ví dụ như gửi nội dung của portlet đến file JSP được định nghĩa trong portlet.xml như tham số khởi tạo view-template).</div>
<div style="text-align: justify;">
Nếu không gọi <b>super.render()</b> thì phương thức ghi đè của chúng ta sẽ không làm việc một cách chính xác. Phương thức này sẽ lấy các đối tượng (object) kiểu RenderRequest và RenderResponse làm tham số.</div>
<div style="text-align: justify;">
RenderRequest và RenderResponse là khác so với ServletRequest và ServletResponse. Portlet không truy cập trực tiếp vào ServletRequest và ServletResponse. RenderRequest và RenderResponse được mở rộng từ giao diện PortletResquest và PortletResponse như hình dưới đây:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-TLGQHFK7zcc/V7UeFU6oUPI/AAAAAAAACIQ/sH9HJcVzwrI2hiJqlynGNqdMQx9i42L4ACLcB/s1600/render-2Blifecycle-2Bmethod.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="231" src="https://3.bp.blogspot.com/-TLGQHFK7zcc/V7UeFU6oUPI/AAAAAAAACIQ/sH9HJcVzwrI2hiJqlynGNqdMQx9i42L4ACLcB/s320/render-2Blifecycle-2Bmethod.png" width="320" /></a></div>
<br /></div>
<div style="text-align: justify;">
Lưu lớp PhaseAndLifecyclePortlet và triển khai nó lên server. Bạn sẽ thấy phương thức <b>init()</b> được gọi trong quá trình triển khai portlet. Portlet container sẽ xoá tất cả các thể hiện đã có của portlet và tạo ra các thể hiện mới. Đây là lúc phương thức <b>init()</b> sẽ được gọi.</div>
<div style="text-align: justify;">
Phương thức vòng đời <b>render</b> sẽ được gọi theo ba kịch bản sau:</div>
<div style="text-align: justify;">
<ul>
<li><b>Kịch bản 01</b>: Phương thức <b>render()</b> được gọi khi trang (Liferay page) được làm tươi (refresh).<br />
Triển khai (deploy) portlet và refresh lại trang nơi mà portlet được đặt (nếu chưa có thì bạn kéo portlet vào), bạn sẽ thấy được các dòng log được ghi ra trên màn hình console của server giống như hình dưới:<br /><a href="https://1.bp.blogspot.com/-FNriAOMZZug/V7UeMrv2BII/AAAAAAAACIU/2upDZMzgUgYPNCw5e-qrk8n0lCTRv1r9wCLcB/s1600/render_in_console.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="27" src="https://1.bp.blogspot.com/-FNriAOMZZug/V7UeMrv2BII/AAAAAAAACIU/2upDZMzgUgYPNCw5e-qrk8n0lCTRv1r9wCLcB/s400/render_in_console.png" width="400" /></a><br />
Quan sát các dòng log, chúng ta thấy rằng mỗi khi trang được refresh thì phương thức <b>render()</b> sẽ được gọi. Để hiểu được sâu hơn, bạn có thể viết vài portlet tương tự và đặt nó vào cùng trang với portlet hiện tại. Đặt các dòng log vào từng phương thức <b>render() </b>của từng portlet.<br />
Refresh lại trang, bạn sẽ quan sát thấy tất cả các phương thức <b>render()</b> của tất cả các portlet đều được gọi. Tuy nhiên, không có gì đảm bảo về thức tự gọi phương thức render của các portlet trên cùng một trang. Điều đó có nghĩa là phương thức render của portlet nào được gọi đầu tiên là không thể dự đoán được.</li>
<li><b>Kịch bản 02</b>: Phương thức render được gọi khi renderURL được gọi.<br />
renderURL là gì?<br />
Các servlet được gọi bở các bảng ánh xạ url của nó (được định nghĩa trong web.xml). Portlet không thể được ánh xạ bởi url trực tiếp giống như servlet. Portlet container cung cấp các thẻ / lớp tiện ích nhận định, chúng sẽ được sử dụng bởi portlet để sinh ra các url. Các url được sinh ra bởi portlet sẽ chỉ (point) đến bản thân nó. Nói các khác, url này sẽ tham chiếu tới portlet tạo ra nó và được gọi là PortletURL. Sau đây là các loại PortletURl khác nhau:<br />
+ renderURL (sẽ gọi phương thức render của portlet)<br />
+ actionURL (sẽ gọi phương thức processAction của portlet)<br />
+ resourceURL (sẽ gọi phương thức serveResource của portlet)<br />
Phương thức vòng đời render() của portlet được gọi khi renderURL của portlet đó được click vào. renderURL chỉ ra portlet nào tạo ra nó. Nó có thể trỏ đến cùng portlet hoặc các portlet khác dựa vào các tham số được đặt trong renderURL. Sau đây là các cách khác nhau để tạo ra renderURL. Chi tiết được giải thích cụ thể trong các bài viết tương ứng:<br />
+ <a href="http://www.opensource-techblog.com/2014/12/create-render-url-by-portlet-tag-in-jsp.html">Lựa chọn 01: tạo ra renderURL bằng thẻ portlet trong JSP</a><br />
+ <a href="http://www.opensource-techblog.com/2014/12/create-render-url-by-java-api-in.html">Lựa chọn 02: tạo ra renderURL bằng Java API trong lớp controller và JSP</a><br />
+ <a href="http://www.opensource-techblog.com/2014/12/create-render-url-by-liferay-tag.html">Lựa chọn 03: tạo ra renderURL bằng thẻ của Liferay</a><br />
+ <a href="http://www.opensource-techblog.com/2014/12/create-render-url-by-java-script-aui.html">Lựa chọn 04: tạo ra renderURL bằng javascript</a></li>
<li><b>Kịch bản 03</b>: phương thức render() được gọi khi phương thức action() của bất kỳ portlet nào trên cùng trang được hoàn thành.<br />
Nhiều portlet có thể được đặt trên cùng một trang (trong Liferay). Khi bất kỳ một portlet nào hoàn thành phương thức processAction() thì phương thức render() của tất cả các portlet khác trên trang đó sẽ được gọi. Trong trường hợp này, người dùng không đã không tương tác một cách trực tiếp với các portlet khác nhưng chúng vẫn sinh ra nội dung. Chúng ta sẽ nhìn thấy điềy này nhiều hơn trong các bài viết cụ thể.</li>
</ul>
<h2>
Kết luận</h2>
<div style="text-align: justify;">
Phương thức vòng đời render() thể hiện cho pha sinh nội dung portlet. Trong pha này, nội dung của portlet sẽ được sinh ra dựa vào trạng thái hiện tại của nó.</div>
<div style="text-align: justify;">
Phương thức này được gọi khi trang chứa nó được refresh hoặc một portlet khác trên cùng trang đó thức hiện xong một hành động (action) hoặc portlet được gọi bởi renderURL từ chính portlet đó hay tư portlet khác.<br />
Để hiển rõ hơn về hai pha (render và action), bạn hãy đọc ở link <a href="https://dev.liferay.com/develop/learning-paths/mvc/-/knowledge_base/6-2/writing-your-first-liferay-application#using-portlet-actions?notice=1" target="_blank">này</a>.</div>
</div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-18864555651204137422016-07-31T16:50:00.002+07:002017-02-06T10:58:37.665+07:00Liferay 6.2: Vòng đời của Portlet - Phương thức init()<div style="text-align: justify;">
<a href="https://2.bp.blogspot.com/-k_2Zrygx3c0/V9P3E5Y_mBI/AAAAAAAACJM/d5Yez1pHj5UB7rbpbT6aQNanRTJvMK22QCLcB/s1600/init%2Bmethod.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://2.bp.blogspot.com/-k_2Zrygx3c0/V9P3E5Y_mBI/AAAAAAAACJM/d5Yez1pHj5UB7rbpbT6aQNanRTJvMK22QCLcB/s1600/init%2Bmethod.png" /></a>Pha khởi tạo của một <b><i>portlet</i></b> được thể hiện bởi phương thức <b><i>init</i></b>(). Khi một <b><i>portlet</i></b> được triển khai, portlet container sẽ xoá bỏ thể hiện (instance) đã có của nó và tạo các thể hiện mới. Lúc này, phương nó sẽ gọi phương thức <b><i>init</i></b>().<br />
<br />
<a name='more'></a></div>
<div style="text-align: justify;">
Giống như <b><i>Servlet</i></b>, <b><i>Portlet</i></b> có thể khởi tạo bất kỳ tài nguyên hoặc các hoạt động khác trong thời gian chạy phương thức <i><b>init</b></i>(). Hay nói một cách ngắn gọn, phương thức <b><i>init</i></b>() là nơi khởi tạo.</div>
<div style="text-align: justify;">
<b><i>Servlet</i></b> đọc các tham số khởi tạo từ tệp <i><b>web.xml</b></i> trong phương thức <b><i>init</i></b>() của nó. Tương tự như vậy, <b><i>Portlet</i></b> đọc các tham số khởi tạo từ tệp <b><i>portlet.xml</i></b> trong pha này. Nói chung thì các tham số khởi tạo được sử dụng để định nghĩa các trang (page flow) trong portlet.<br />
Chúng ta sẽ xem các phương thức vòng đời làm việc như thế nào bằng cách tạo ra một portlet <b><i>Liferay MVC</i></b>. Đặt tên portlet là <i><b>portlet-phase-lifecycle</b></i>, eclipse sẽ tự động thêm <i><b>-portlet</b></i> vào cuối tên của project.<br />
Ta có một lớp được gọi là <i><b>com.opensource.techblog.portlet.PhaseAndLifecyclePortlet</b></i>. Cấu trúc của <i><b>portlet</b></i> sẽ giống như hình sau:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-oZ66ZelaY4Q/V53IgdJrs0I/AAAAAAAACHQ/UR3qMW6oSqU7n-0HuyvRz3fP7QblIctJwCLcB/s1600/project_Structure.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://2.bp.blogspot.com/-oZ66ZelaY4Q/V53IgdJrs0I/AAAAAAAACHQ/UR3qMW6oSqU7n-0HuyvRz3fP7QblIctJwCLcB/s320/project_Structure.png" width="275" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Cấu trúc của project</td></tr>
</tbody></table>
Như tác giả đã đề cập trong bài viết <a href="http://www.opensource-techblog.com/2012/08/creating-custom-liferay-mvc-portlet.html">Liferay MVC Portlet</a>:<br />
<ul>
<li><b><i>GenericPortlet</i></b> cài đặt giao diện <i><b>Portlet</b></i></li>
<li><b><i>LiferayPortlet</i></b> kế từ từ <i><b>GenericPortlet</b></i> và cung cấp thêm các phương thức mới.</li>
<li><i><b>MVCPortlet</b></i> được định nghĩa bởi Liferay. Nó là mở rộng của <i><b>LiferayPorrtlet</b></i> và cung cấp kiến trúc <i><b>MVC</b></i> bằng cách thêm vào vài phương thức nữa.</li>
<li><i><b>PhaseAndLifecyclePortlet</b></i> (portlet mà chúng ta tạo ra) kế thừa từ <i><b>MVCPortlet</b></i>.</li>
</ul>
<h2>
Phương thức init</h2>
Để hiểu được chu trình làm việc của phương thức <i><b>init()</b></i>, chúng ta sẽ ghi đè nó trong portlet của chúng ta. Có hai phiên bản của phương thức <i><b>init()</b></i> trong hệ thống phân cấp lớp của Liferay.<br />
<h3>
Phương thức init trong MVCPortlet</h3>
<ul>
<li>Cú pháp: <i>public void init() throws PortletException</i></li>
<ul>
<li>Phương thức này không có tham số.</li>
</ul>
</ul>
<h3>
Phương thức init trong GenericPortlet</h3>
<ul>
<li>Cú pháp: <i>public void init(PortletConfig config) throws PortetException</i></li>
<ul>
<li>Phương thức này có một tham số là <i><b>PortletConfig</b></i></li>
<li>Đối tượng của <i><b>PortletConfig</b></i> được sử dụng để đọc các tham số cấu hình (trong tệp <i><b>portlet.xml</b></i>)</li>
<li><i><b>PortletConfig</b></i> tương tự như <i><b>ServletConfig</b></i> (cái được sử dụng để đọc các tham số cấu hình portlet từ tệp <i><b>web.xml</b></i>)</li>
</ul>
</ul>
Sau khi ghi đè hai phương thức init này, lớp của chúng ta sẽ trông giống như đoạn code sau đây:<br />
<script src="https://gist.github.com/programmerit/cd1cf086c50acb252884bbc1565cea4b.js"></script><br />
<h3>
Giải thích</h3>
<ul>
<li><i><b>LogFactoryUtil</b></i> là lớp tiện ích được cung cấp bởi Liferay để ghi log của lớp hiện tại.</li>
<li>Chúng ta đã ghi đè cả hai phiên bản của phương thức <i><b>init</b></i>() và thêm vào đó vài dòng ghi log. Các lệnh log này sẽ hiển thị trong màn hình <i>console</i> khi portlet được triển khai.</li>
<li>Trong phiên bản thứ hai của phương thức <i><b>init</b></i>(), chúng ta sẽ đọc tham số khởi tạo được gọi là "<i><b>view-template</b></i>" và ghi nó vào log. tham số khởi tại "<i><b>view-template</b></i>" được định nghĩa trong tệp <i><b>portlet.xml</b></i> và được sử dụng để định nghĩa đường dẫn đến file jsp.</li>
</ul>
Sau khi thực hiện những thay đổi này và triển khai portlet. Đảm bảo rằng máy chủ của bạn vẫn đang chạy. Trong khi triển khai, portlet container sẽ gọi phương thức <i><b>init</b></i>() và bạn sẽ nhìn thấy những dòng log mà mình ghi ra ở màn hình <i>console</i>.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-w-khiU-55v4/V53JXkjJlRI/AAAAAAAACHY/2M83nyuvvwYKMhgp2eGp0wKwv2iWpwSBgCLcB/s1600/init_in_console.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="68" src="https://2.bp.blogspot.com/-w-khiU-55v4/V53JXkjJlRI/AAAAAAAACHY/2M83nyuvvwYKMhgp2eGp0wKwv2iWpwSBgCLcB/s400/init_in_console.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Log được ghi ra</td></tr>
</tbody></table>
<ul>
<li>Như bạn có thể thấy trong quá trình triển khai portlet này, phương thức <i><b>init</b></i>() ghi đè của lớp <i><b>GenericPortlet</b></i> được gọi trước theo sau là phương thức <i><b>init</b></i>() của phương thức <i><b>MVCPortlet</b></i>.</li>
<li>Có điều này là do <i><b>GenericPortlet</b></i> có vị trí cao hơn so với <i><b>LiferayMVCPortlet</b></i> trong sơ đồ phân cấp lớp.</li>
<li>Các dòng log cũng chỉ ra giá trị của tham số khởi tạo (<i><b>view-template</b></i> là <i><b>/view.jsp</b></i>) cái mà được định nghĩa trong file <i><b>portlet.xml</b></i>. Chúng ta có thể đọc tất cả các phương thức khởi tạo trong phương thức <i><b>init</b></i>().</li>
</ul>
Kéo portlet này vào một vài trang trong Liferay. Khi refresh lại các trang này, phương thức <i><b>init()</b></i> sẽ không được gọi lại nữa. Nó chỉ được gọi khi portlet được khởi tạo bởi portlet container (trong quá trình triển khai lên server mà thôi). Điều này có nghĩa là mỗi khi chúng ta triển khai portlet thì phương thức <i><b>init</b></i>() sẽ được gọi.<br />
<h2>
Kết luận</h2>
Phương thức vòng đời <i><b>init</b></i>() sẽ được gọi bởi portlet container trong quá trình khởi tạo portlet.<br />
Nó được gọi chỉ duy nhất một lần trong lúc triển khai portlet lên server.<br />
Nó là nơi để chúng ta định nghĩa các tham số tại thời điểm khởi tạo portlet.<br />
Bạn có thể đọc bài viết gốc tại địa chỉ: <a href="http://www.opensource-techblog.com/2014/12/portlet-lifecycle-method-init.html">http://www.opensource-techblog.com/2014/12/portlet-lifecycle-method-init.html</a></div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-22469283766522527302016-07-28T15:01:00.001+07:002017-02-06T10:58:27.476+07:00Liferay 6.2: Giới thiệu các pha và vòng đời của portlet<div style="text-align: justify;">
<a href="https://4.bp.blogspot.com/-8iL2EoJ6qwc/V566z7N-6XI/AAAAAAAACHw/woFRov3lBWg0b-TTlX3gVOcgYlc0WM98ACLcB/s1600/portlet%2Blifecycle.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://4.bp.blogspot.com/-8iL2EoJ6qwc/V566z7N-6XI/AAAAAAAACHw/woFRov3lBWg0b-TTlX3gVOcgYlc0WM98ACLcB/s1600/portlet%2Blifecycle.png" /></a><b><i>Portlet</i></b> thực hiện một hành động duy nhất trong mỗi chu trình thực thi. Các chu trình này được biết đến là các pha. Mỗi pha/chu trình được thể hiện bởi các phương thức tương ứng trong lớp controller (portlet class).</div>
<a name='more'></a><br />
<div style="text-align: justify;">
Trong <b><i>Servlet</i></b>, servlet container gọi phương thức <b><i>service</i></b>() để xử lý các yêu cầu của người dùng. Các portlet được thiết ekes để đặt cùng với nhau và cùng với các portlet khác trong cùng một trang (để sinh ra một trang web đầy đủ). Rất có thể người dùng không tương tác trực tiếp với một portlet.</div>
<div style="text-align: justify;">
Ví dụ, nếu một phương tức action được thực hiện đầy đủ trong một portlet thì phương thức <b>render</b>() ủa nó sẽ được gọi cùng với phương thức <b><i>render</i></b>() của tất cả các phương thức khác trên trang hiện tại. Trong trường hợp này, các portlet khác trong cùng trang sẽ vẫn sinh ra câu trả lời mặc dùng người dùng không trực tiếp tương tác với chúng.</div>
<div style="text-align: justify;">
Do cách hoạt động như trên, trong tài liệu mô tả kỹ thuật của portlet, người ta định nghĩa nhiều hơn một phương thức để xử lý các yêu cầu của người dùng. Các phương thức này đại diện cho các pha tương ứng trong Portlet.</div>
<div style="text-align: justify;">
Hai phương thức đầu tiên đã được định nghĩa trong <a href="http://www.oracle.com/technetwork/java/index-raji-test-141933.html" target="_blank">JSR-168</a> (Portlet 1.0) là:<br />
<ul>
<li>Pha <b><i>Render</i></b></li>
<li>Pha <b><i>Action</i></b></li>
</ul>
</div>
<div style="text-align: justify;">
Hai pha tiếp theo được thêm vào trong tiêu chuẩn <a href="http://www.oracle.com/technetwork/java/jsr286-141866.html" target="_blank">JSR-286</a> (Portlet 2.0) gồm:<br />
<ul>
<li>Pha <b><i>Event</i></b></li>
<li>Pha <i><b>Resource</b> <b>Serving</b></i></li>
</ul>
</div>
<div style="text-align: justify;">
JSR-168 có hai pha trong khi JSR-286 có bốn pha (JSR-286 là tổng quát hơn JSR-168).</div>
<h2>
Các pha trong potlet</h2>
<div style="text-align: justify;">
Các phương thức sau đây thể hiện cho các pha của portlet. Các phương thức này cũng được biết đến là vòng đời của portlet.<br />
<ul>
<li><b><i>init</i></b>(): Phương thức này sẽ được gọi khi portlet được triển khai và khởi tạo bởi portlet container.</li>
<li><b><i>render</i></b>(): Phương thức này được gọi để sinh ra nội dung trên trang web. Hay còn được gọi là pha Represent Render.</li>
<li><b><i>processAction</i></b>(): Phương thức này được gọi khi bất kỳ thao các nào được gọi (thêm, sửa, xoá, ...).</li>
<li><b><i>processEvent</i></b>(): Phương thức này được gọi khi bất kỳ một sự kiện nào được kích hoạt.</li>
<li><b><i>serveResource</i></b>(): Phương thức này sẽ được gọi đến khi bất kỳ tài nguyên là được yêu cầu phục vụ bởi một URL yêu cầu tài nguyên (Resource URL).</li>
<li><b><i>destroy</i></b>(): Phương thức này sẽ được gọi khi portlet được xoá bỏ.</li>
</ul>
</div>
<div style="text-align: justify;">
Các phương thức vòng đời hay các pha của portlet được quản lý bởi Portlet Container. Nó chịu trách nhiệm cho các công việc sau:</div>
<ul>
<li>Tải các lớp của portlet</li>
<li>Tạo và duy trì các thể hiện (instance) của portlet.</li>
<li>Khởi tạo portlet</li>
<li>Gửi các yêu cầu của người dùng đến các thể hiện của portlet.</li>
<li>Huỷ các thể hiện của portlet khi nó bị xoá khỏi Portlet container.</li>
</ul>
<div style="text-align: justify;">
Nếu bạn đã quen với khái niệm servlet, bạn có thể dễ dàng hiểu portlet vì có sự tương quan giữa các phương thức vòng đợi của portlet và servlet.</div>
<div style="text-align: justify;">
Hình sau đây sẽ mô tả các phương thức vòng đời của servlet và portlet.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-nXp9dfBBWVc/V5m4X340UHI/AAAAAAAACG4/LZK1AV4Jol4AdmhlW8rpaS1ZjqCl84wBwCLcB/s1600/lifecycle-2Bmethods.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="220" src="https://4.bp.blogspot.com/-nXp9dfBBWVc/V5m4X340UHI/AAAAAAAACG4/LZK1AV4Jol4AdmhlW8rpaS1ZjqCl84wBwCLcB/s320/lifecycle-2Bmethods.png" width="320" /></a></div>
<div style="text-align: justify;">
<ul>
<li>Không có quan hệ trực tiếp giữa các phương thức vòng đời của portlet và servlet.</li>
<li>Mô hình này chỉ để hiểu các phương thức vòng đời khi đặt cạnh nhau.</li>
<li>Cả Portlet và Servlet, các phương thức <b><i>init</i></b>() và <b><i>destroy</i></b>() được gọi đến bởi container tương ứng (Portlet container và Servlet container).</li>
<li>Đối với Servlet, tất cả các yêu cầu được phục vụ bởi phương thức <b><i>service</i></b>().</li>
<li>Còn đối với Portlet, yêu cầu của người dùng được phục vụ bởi các phương thức khác nhau như phương thức <b><i>render</i></b>(), <b><i>processAction</i></b>(), <b><i>processEvent</i>()</b> và <b><i>serveResource</i>()</b> dựa vào pha hiện tại của portlet.</li>
</ul>
</div>
<h2>
Giải thích chi tiết các phương thức vòng đời của portlet</h2>
<div style="text-align: justify;">
Tác giả của bài viết gốc đã có các bài viết giải thích chi tiết các phương thức vòng đời của portlet. Hiện tại, mình chỉ đưa link của các bài viết này bằng Tiếng Anh. Khi nào có thời gian rảnh, mình sẽ dịch các bài này sang Tiếng Việt để phục vụ các bạn. Bạn có thể đọc chúng ở các địa chỉ sau:<br />
<ul>
<li><a href="http://chingovan.blogspot.com/2016/07/liferay-phuong-thuc-init.html" rel="" target="_blank">Pha khởi tạo của Portlet / Phương thức vòng đời – init()</a></li>
<li><a href="http://chingovan.blogspot.com/2016/08/liferay-phuong-thuc-render.html" target="_blank">Vòng đời của Portlet - Phương thức render()</a></li>
<li><a href="http://chingovan.blogspot.com/2016/09/liferay-phuong-thuc-action.html" target="_blank">Vòng đời của Portlet - Phương thức processAction()</a></li>
<li><a href="http://chingovan.blogspot.com/2016/09/liferay-phuong-thuc-serve-resource.html" target="_blank">Vòng đời của Portlet - Phương thức serveResource()</a></li>
<li>Portlet Lifecycle method – processEvent() – Available soon</li>
</ul>
</div>
<h2>
Kết luận</h2>
<div style="text-align: justify;">
Đây là một bài viết đáng đọc, nó cung cấp cho các bạn một cái nhìn tổng quát về hoạt động của một portlet.</div>
<div style="text-align: justify;">
Bài viết được dịch từ trang <a href="http://www.opensource-techblog.com/2014/12/introduction-to-portlet-phases-and.html">http://www.opensource-techblog.com/2014/12/introduction-to-portlet-phases-and.html</a>.</div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-19519713151589193542016-06-22T15:08:00.000+07:002017-02-06T10:58:19.389+07:00Liferay 6.2: Cấu hình portlet<div style="text-align: justify;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-OiIU93MGh2A/V3AhdqcBlwI/AAAAAAAAB-8/3FE--Huhz7YKX4UL_grOJ6KrJTLdGA1-gCLcB/s1600/portlet-configuration.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://4.bp.blogspot.com/-OiIU93MGh2A/V3AhdqcBlwI/AAAAAAAAB-8/3FE--Huhz7YKX4UL_grOJ6KrJTLdGA1-gCLcB/s1600/portlet-configuration.png" /></a></div>
Sau khi tạo ra một portlet, bạn hi vọng nó sẽ được sử dụng ở nhiều nơi. Đây thực sự là một hi vọng chính đáng. Tuy nhiên, nếu bạn tạo ra một portlet cứng nhắc sẽ không thể tái sử dụng được. Do vậy, trong quá trình thiết kế portlet, bạn phải đảm bảo rằng portlet của mình thực sự linh hoạt và dễ dàng tùy biến theo từng nhu cầu cụ thể.</div>
<div style="text-align: justify;">
Trong bài viết này, mình sẽ hướng dẫn các bạn cách mở rộng cấu hình portlet.</div>
<br />
<a name='more'></a><h2 style="text-align: justify;">
Giới thiệu</h2>
<div style="text-align: justify;">
Nếu đã từng làm việc với các portlet của Liferay, bạn có thể chỉnh sửa các tham số của mỗi portlet bằng cách nhấn vào biểu tượng Cấu hình (Configuration) ở góc trên bên trái của mỗi portlet và chọn Cấu hình (Configuration).<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-r61SWGT3pho/V3Ak1RM0WZI/AAAAAAAAB_c/uep6P2-TeJ0b5PoBMm7-NgEclBFEW_ryACLcB/s1600/configuration.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://2.bp.blogspot.com/-r61SWGT3pho/V3Ak1RM0WZI/AAAAAAAAB_c/uep6P2-TeJ0b5PoBMm7-NgEclBFEW_ryACLcB/s1600/configuration.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Nhấn vào "Cấu hình" để mở cửa sổ cấu hình như hình dưới</td></tr>
</tbody></table>
Sẽ có một cửa sổ được mở lên với hai tab mặc định là Quyền hạn (Permission) và Chia sẻ (Share). Ngoài hai tab mặc định trên, một số portlet còn có thêm tab Cài đặt (Setup), tab này sẽ chứa nội dung cấu hình được thêm vào. Khi bạn viết các trang JSP để sửa đổi cấu hình của portlet thì nội dung bạn thêm vào sẽ được hiển thị bên trong tab này.</div>
<div style="text-align: justify;">
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://1.bp.blogspot.com/-arDjKrR9z2k/V3AmFI_a44I/AAAAAAAAB_s/Kxa1ix8miaQtQazJyi5d3sxeFTjq-22lQCLcB/s1600/default%2Btabs.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="239" src="https://1.bp.blogspot.com/-arDjKrR9z2k/V3AmFI_a44I/AAAAAAAAB_s/Kxa1ix8miaQtQazJyi5d3sxeFTjq-22lQCLcB/s320/default%2Btabs.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Các tab mặc định</td></tr>
</tbody></table>
</div>
<h2 style="text-align: justify;">
Cấu hình</h2>
<div>
<div style="text-align: justify;">
Có hai phần mà chúng ta cần quan tâm khi muốn thêm các tham số cấu hình cho một portlet đó là <i>Front-end</i> (jsp) và <i>Back-end</i> (java class). Chúng ta sẽ đi xem xét từng phần một nhé.</div>
</div>
<h3>
Back-end</h3>
<div>
<div style="text-align: justify;">
Chúng ta sẽ tạo ra lớp <i>CustomConfigurationAction</i> được kế thừa từ lớp <i>DefaultConfigurationAction</i>. Lớp <i>CustomConfigurationAction</i> có tác dụng điều hướng và xử lý yêu cầu mà <i>Front-end</i> gửi xuống. Để cho hệ thống biết lớp <i>CustomConfigurationAction</i> đảm nhận nhiệm cấu hình của một portlet nào đó. Ta sẽ thêm thẻ <i><configuration-action-class>com.blogspot.chingovan.CustomConfiguration</configuration-action-class<</i> vào trong file <i>liferay-portlet.xml</i> như sau (dòng số 9):</div>
</div>
<div>
<script src="https://gist.github.com/programmerit/46c00fcda35dfa4c4edccd0445ac7f2a.js"></script></div>
<div>
<div style="text-align: justify;">
Ở lớp CustomConfigurationAction, ta cần phải ghi đè (override) hai phương thức của lớp cha là <i>render</i> và <i>processAction</i>.</div>
</div>
<div>
<ul>
<li>Phương thức <i>render</i> sẽ nói cho hệ thống file jsp nào sẽ được gọi khi bạn mở giao diện cấu hình portlet.</li>
<li>Phương thức <i>processAction</i> sẽ xử lý các yêu cầu mà front-end gửi xuống.</li>
</ul>
</div>
<div>
<script src="https://gist.github.com/programmerit/4b8d2637b034305fb09ea1a351003467.js"></script><br />
<div style="text-align: justify;">
Trong đoạn code trên, khi bạn mở cấu hình của portlet sẽ thấy một tab trắng (Cài đặt (Setup)) được thêm vào như hình dưới:</div>
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://3.bp.blogspot.com/-lSbv5hyRtDU/V3ApiD-DXoI/AAAAAAAACAE/8FQnTKdnfEQPsG_n0fuH_5Q1VzaPqev4QCLcB/s1600/add%2Bsetup%2Btab.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="153" src="https://3.bp.blogspot.com/-lSbv5hyRtDU/V3ApiD-DXoI/AAAAAAAACAE/8FQnTKdnfEQPsG_n0fuH_5Q1VzaPqev4QCLcB/s320/add%2Bsetup%2Btab.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Thêm mới tab Cài đặt (Setup)</td></tr>
</tbody></table>
<h3>
Front-end</h3>
<div>
<div style="text-align: justify;">
Front-end là một file jsp (ví dụ <i>edit_configuration.jsp</i>) được điều hướng đến bởi phương thức <i>render</i> trong lớp <i>CustomConfigurationAction</i>. Trong file này, ta sẽ viết một form với các trường là các tham số cấu hình của portlet. Ví dụ, ta sẽ lưu lại một lời chào chẳng hạn (<i>greeting</i>). Rồi tham số này sẽ được hiển thị trên portlet của chúng ta.</div>
</div>
<div>
<div style="text-align: justify;">
Hiện tại, tab Cài đặt không có gì cả (hình trên). Nội dung của tab này chính là nội dung của file <i>edit_configuration.jsp </i>mà phương thức <i>render</i> (trong lớp <i>CustomConfigurationAction</i> chuyển đển). Chúng ta sẽ xem nội dung của file <i>edit_configuration.jsp</i> trong phần tiếp.</div>
</div>
<h2>
Ghi và đọc các tham số</h2>
<h3>
Ghi các tham số</h3>
<div>
<div style="text-align: justify;">
Các tham số sẽ được gửi từ Front-end, cụ thể là trong form ở file <i>edit_configuration.jsp</i> với nội dung như sau:</div>
<script src="https://gist.github.com/programmerit/69ccdc053e71fcd2d1a668fedf4da9fc.js"></script><br />
<div style="text-align: justify;">
Giao diện cập nhật tham số của portlet sẽ như sau:</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-x5a2PDG6jD0/V3At5X3Em5I/AAAAAAAACAY/0SwWmvpY7CEpOsMBT7d2QPlpPNgIgQDaACLcB/s1600/configuration-gui.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="245" src="https://2.bp.blogspot.com/-x5a2PDG6jD0/V3At5X3Em5I/AAAAAAAACAY/0SwWmvpY7CEpOsMBT7d2QPlpPNgIgQDaACLcB/s320/configuration-gui.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Giao diện sử tham số cấu hình portlet</td></tr>
</tbody></table>
<div style="text-align: justify;">
Ta nhập dữ liệu và nhấn nút "Ghi lại". Lúc này, phương phương thức <i>processAction</i> của lớp <i>CustomConfigurationAction</i> sẽ được gọi để xử lý và lưu trữ các cấu hình này. Hãy xem đoạn code sau:</div>
</div>
<div>
<script src="https://gist.github.com/programmerit/f58b2c4b38be94f3e4cb9f2b88451d0e.js"></script></div>
<div>
<div style="text-align: justify;">
Trong đoạn code trên, chúng ta cần quan tâm một số dòng sau đây:</div>
</div>
<div>
<ul>
<li>Dòng 6-7: Lấy cấu hình hiện tại của portlet.</li>
<li>Dòng 10: Lấy các giá trị được gửi xuống.</li>
<li>Dòng 13: Thêm các giá trị mới vào vào biến cấu hình của portlet.</li>
<li>Dòng 16: Lưu trữ các tham số cấu hình của portlet vào cơ sở dữ liệu.</li>
<li>Dòng 19: Gửi lại thông báo đã cập nhật thành cộng</li>
</ul>
<div>
<div style="text-align: justify;">
Sau khi thực hiện lưu trữ, hãy mở cơ sở dữ liệu lên và tìm bảng PortletPreferences. Chúng ta sẽ thấy, các tham số cấu hình của một portlet được lưu trữ dưới dạng <i class="">xml</i> như hình sau:</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-gA2hgtBccsk/V3AwDjbm3NI/AAAAAAAACAo/sD9lzK0v6BkIFaXwUrOGTZLSqCyMnqZAACLcB/s1600/portlet-configuration-in-database.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="56" src="https://2.bp.blogspot.com/-gA2hgtBccsk/V3AwDjbm3NI/AAAAAAAACAo/sD9lzK0v6BkIFaXwUrOGTZLSqCyMnqZAACLcB/s640/portlet-configuration-in-database.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Cấu hình portlet được lưu trong cơ sở dữ liệu</td></tr>
</tbody></table>
</div>
</div>
<h3>
Đọc các tham số</h3>
<div>
<div style="text-align: justify;">
Các tham số cấu hình của portlet sẽ được sử dụng vào các mục đích của bạn. Vậy, làm thế nào để ta có thể đọc được các tham số này từ cơ sở dữ liệu? Thật may, Liferay đã hỗ trợ chúng ta rất nhiều và bạn chỉ cần truyền đúng tham số là sẽ lấy được giá trị mong muốn.</div>
</div>
<div>
<div style="text-align: justify;">
Đoạn code dưới đây minh họa cách đọc cấu hình của một portlet (dòng) và trong ví dụ này, ta chỉ sử dụng tham số trong trường hợp vô cung đơn giản đó là in ra tham số đã ghi.</div>
<script src="https://gist.github.com/programmerit/cb69ae0b27f9570fad926dfeb5c76222.js"></script><br />
<div style="text-align: justify;">
Trong đoạn code trên, chúng ta để ý ở dòng số 4. Ta lấy giá trị của các tham số cấu hình từ đối tượng <i>portletPreferences</i>. Đối tượng này được cung cấp sẵn. Hình dưới đây là kết qủa cuối cùng:</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://1.bp.blogspot.com/-E7TSp_v1KSU/V3AxqV37vUI/AAAAAAAACA8/_1SM1p_lfLQsy4PG9OeorVPK-_tQEePWQCLcB/s1600/reading-configuration-value.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="65" src="https://1.bp.blogspot.com/-E7TSp_v1KSU/V3AxqV37vUI/AAAAAAAACA8/_1SM1p_lfLQsy4PG9OeorVPK-_tQEePWQCLcB/s320/reading-configuration-value.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Giá trị của một tham số cấu hình được in ra màn hình</td></tr>
</tbody></table>
</div>
<h2>
Kết luận</h2>
<div>
<div style="text-align: justify;">
Bài viết này giúp cho portlet của bạn linh hoạt hơn và áp dụng được nhiều nơi mà không cần phải sửa lại mã nguồn. Nó là một hướng dẫn đơn giản và hướng đến những người mới bắt đầu làm quen với việc phát triển ứng dụng trên nền tảng Liferay.<br />
Bạn có thể tải mã nguồn đầy đủ tại <a href="https://github.com/programmerit/liferay-tutorial/tree/master/configuration-portlet" target="_blank">đây</a>.</div>
</div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-51397162661505010002016-06-03T13:37:00.001+07:002017-05-16T10:17:17.957+07:00Java: Using POI library to read and write Excel data<a href="http://4.bp.blogspot.com/-EWneJbeDnQQ/VcDu6u0TA6I/AAAAAAAABQ8/V010-5F99aM/s1600/read-write.png" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="133" src="https://4.bp.blogspot.com/-EWneJbeDnQQ/VcDu6u0TA6I/AAAAAAAABQ8/V010-5F99aM/s200/read-write.png" width="200" /></a><br />
<a href="http://chingovan.blogspot.com/2015/08/doc-ghi-du-lieu-excel-trong-java.html" target="_blank">Vietnam version</a><br />
<div style="text-align: justify;">
After reading the article title, you can easily guess the intentions of this post, right? Reading and writing excel data are the often actions in our jobs, Therefore, how can you read and write this data by the simplest way? This post is going to answer the above question.</div>
<a name='more'></a><br />
<h2>
Apache POI Project</h2>
<div style="text-align: justify;">
The mission of Apache POI project is creating and maintaining the Java APIs which work on the different file format as Office Open XML (OOXML) standard and OLE 2 format of Microsoft corps. In simple terms, this project helps to read and write data from excel format in the Java programs. Moreover, you can read and write data wit MS Word and MS Powerpoint documents as well. You can get the details at <a href="https://poi.apache.org/" target="_blank">here</a> and get the library at <a href="https://poi.apache.org/download.html" target="_blank">this link</a>.</div>
<h2>
Read and write</h2>
<div style="text-align: justify;">
Before working with the Excel filé, we have to understand its structure and then we will read and write them. Of course, if you had had the base computing knowledge, you knew the structure of an Excel file, right? It contains the following components (click <a href="https://support.office.com/en-nz/article/Excel-specifications-and-limits-16c69c74-3d6a-4aaf-ba35-e6eb276e8eaa" target="_blank">here</a> to get more details):</div>
<div style="text-align: justify;">
<ol>
<li><i>Workbook</i>: workbook is an Excel file. Now, we will call workbook instead Excel file.</li>
<li><i>Worksheet</i>: we can call <i>sheet</i>. A workbook includes many worksheets. The names of them are different.</li>
<li><i>Row</i>: a <i>worksheet</i> contains many <i>Rows</i>. The rows are numbered from 0 to end.</li>
<li><i>Cell</i>: a row comprises many <i>Cells</i>. Each <i>Cell</i> will hold the data what we want to read and wire. a <i>Cell</i> is the smallest unit in a workbook.</li>
</ol>
</div>
<div style="text-align: justify;">
In general, if you want to read and write excel data, you need to locate the Cell address.</div>
<h3>
Reading</h3>
<div style="text-align: justify;">
Firstly, we will start with the easy task. Assume, we have a workbook which contains the information of the students in a class. In my example workbook (<a href="https://github.com/programmerit/liferay-tutorial/blob/master/poi/resources/data.in.en.xls">here</a>), it has five columns (<i>Code</i> (String), <i>Fullname</i> (String), <i>Birthday</i> (Date), <i>Gender</i> (Integer) and <i>Address</i> (String)). Now, we will read these data. Let see the following snippet:</div>
<div>
<script src="https://gist.github.com/programmerit/ca90ef9375734f1ce6b6.js"></script></div>
<div style="text-align: justify;">
As you can see, we will focus the <i>readStudent</i> method (from line 37th to 103rd). I will explain some line what we need to consider:</div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
<ul>
<li>Line 42nd: POI library only allows creating a workbook from an instance of <i>InputStream</i> class. Therefore, we have to create an InputStream object from original file. </li>
<li>Line 44th: allows creating a <i>workbook</i> from the input stream.</li>
<li>Line 49th: gets the first <i>sheet</i> in the <i>workbook</i>.</li>
<li>Line 55th-85th: I will get the rows on the first sheet and get the data in each cell in these rows.</li>
</ul>
</div>
<div style="text-align: justify;">
At the moment, I will comeback <a href="https://github.com/programmerit/liferay-tutorial/blob/master/poi/src/com/blogspot/chingovan/poi/Test.java" target="_blank">Test</a> class and run it again. Surprise!, the information of the students showed on the console, right? In the next session, we will know how to write data into a workbook.</div>
<h3>
Write</h3>
<div style="text-align: justify;">
Opposite reading, the order of writing is i) create a workbook, ii) create a worksheet, iii) create a row and iv) create a cell at last. The following snippet will store data into a workbook.</div>
<script src="https://gist.github.com/programmerit/e3c025a96032ce555f91.js"></script><br />
<div style="text-align: justify;">
We will consider <i>writeStudent </i>method (from line 37th to 128th). I will analyse some lines as:</div>
<div style="text-align: justify;">
<ul>
<li>Line 44th: create a <i>workbook</i> in temporary memory .</li>
<li>Line 49th: create a worksheet named <i>Students</i>. This is the first <i>sheet</i> of this workbook.</li>
<li>Line 54th: create a row in this sheet. This row is the header one.</li>
<li>Line 78th - 108th: save the students' information in each row.</li>
</ul>
</div>
<div style="text-align: justify;">
Now, let run <a href="https://github.com/programmerit/liferay-tutorial/blob/master/poi/src/com/blogspot/chingovan/poi/Test.java" target="_blank">Test</a> again. Fortunately, a workbook will be created with your data.</div>
<h2>
Conclusion</h2>
<div style="text-align: justify;">
<ul>
<li>The writing and reading Excel data are the frequency jobs. So, this post is really useful.</li>
<li>In fact, this post only uses the basic feature of Apache POI library. I will introduce the quality posts to you in the future.</li>
<li>You can get the source code at <a href="https://github.com/programmerit/liferay-tutorial/tree/master/poi" target="_blank">here</a>.</li>
</ul>
</div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-43180133457749452822016-05-30T13:18:00.001+07:002017-02-06T10:58:13.381+07:00Liferay 6.2: Window popup<div style="text-align: justify;">
<a href="https://chingovan.blogspot.com/2016/05/liferay-cua-so-popup.html"><b>Vietnam version</b></a></div>
<div style="text-align: justify;">
<a href="https://4.bp.blogspot.com/-eDpCi1SgDTU/V0fRb9oO4NI/AAAAAAAAB9Q/vi2YSAARn4c9Hj1q3ZGt-VWO_k1SmBB1QCLcB/s1600/window%2Bpopup.png" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://4.bp.blogspot.com/-eDpCi1SgDTU/V0fRb9oO4NI/AAAAAAAAB9Q/vi2YSAARn4c9Hj1q3ZGt-VWO_k1SmBB1QCLcB/s1600/window%2Bpopup.png" /></a><br />
<br />
Window popup is a utility which was built-in <i>Liferay</i> Framework. <i>Liferay</i> itself also used this utility in many parts. For examples: Window popup appears in permission interface or portlet configurations (if you click on gear icon at top left portlet, the windows will appear which allows changing the configurations).<br />
In general, window popup is useful in the UI design process. So, how to use it. This post will help you</div>
<br />
<a name='more'></a>The example in this post was tested on <i>Liferay 6.2</i>. In this version, Liferay had simplified openning window popup than the previous ones. I will show you two ways to open window popup. They are the built-in utilities and using <i>AUI (AlloyUI)</i>.<br />
<h2>
The built-in utilities</h2>
<div>
Liferay has provided <span style="font-family: "courier new" , "courier" , monospace; text-align: justify;"><aui:button /></span><span style="text-align: justify;">, </span><span style="font-family: "courier new" , "courier" , monospace; text-align: justify;"><aui:nav-item /></span><span style="text-align: justify;"> và </span><span style="font-family: "courier new" , "courier" , monospace; text-align: justify;"><liferay-ui:icon /></span><span style="text-align: justify;">, tag for us. You can open window popup if the value of its <span style="font-family: "courier new" , "courier" , monospace;">useDialog</span> attribute is "<span style="font-family: "courier new" , "courier" , monospace;">true</span>". Now, we will consider the way how it work.</span></div>
<div style="text-align: justify;">
Assume, we have view.jsp file which contains three above tags and <i>detail.jsp</i> file will be showed in window popup. Following is the container of the <i>detail.jsp</i> file.</div>
<br />
<div style="text-align: justify;">
As you can see, the container of <i>detail.jsp</i> is very easy to understand. We only get a value with parameter key from the <i>request</i> (line 3rd) and show it on screen (line 6th).</div>
<h3>
AUI Button Tag</h3>
<div style="text-align: justify;">
The <span style="font-family: "courier new" , "courier" , monospace;"><a href="https://docs.liferay.com/portal/6.2/taglibs/aui/button.html" target="_blank"><aui:button /></a></span> tag is an useful tag to create the nice buttons. Its importance attributes are:</div>
<div style="text-align: justify;">
<ul>
<li><span style="background-color: white;"><i>href</i>: its value is a URL. If you click on a button, the browser will redirect by this URL. If the value of <span style="font-family: "courier new" , "courier" , monospace;">userDialog</span> is <span style="font-family: "courier new" , "courier" , monospace;">true</span> (<i>userDialog="true"</i>), a window popup will be opened and the redirected page will be showed on it.</span></li>
<li><span style="background-color: white;"><i>icon</i>: show an icon within it.</span></li>
<li><span style="background-color: white;"><i>name</i>: the name of the button.</span></li>
<li><span style="background-color: white;"><i>type</i>: the type of button, the acceptable value are </span><span style="font-family: "courier new" , "courier" , monospace;">button</span>, <span style="font-family: "courier new" , "courier" , monospace;">submit</span>, <span style="font-family: "courier new" , "courier" , monospace;">cancel</span> and <span style="font-family: "courier new" , "courier" , monospace;">reset</span>.</li>
<li><span style="background-color: white;"><i>useDialog</i>: Whether we open a window popup to load a new page?</span></li>
<li><span style="background-color: white;"><i>value</i>: the lable of the button and it is the title of window popup too.</span></li>
</ul>
</div>
<div style="text-align: justify;">
Let see the following example:</div>
<br />
<div style="text-align: justify;">
Now, let deploy your created portlet. You will see a button and click on it. A window popup will appear. The content of this window is the same the opener one. But, the content of the portlet is different. Because the opener window (window contains the button) is showing the content of <i>view.jsp</i> file, but the window popup is presenting the content of <i>detail.jsp</i> file.<br />
If the window popup contains the whole page, it is difficult to see. The window popup only contains the content of the portlet. The following snippet will resolve the above request:</div>
<br />
<div style="text-align: justify;">
The diffirence of the two snippet is at line 4th. I have added <span style="font-family: "courier new" , "courier" , monospace;">windowState</span> attribute (<i>windowState="<%= LiferayWindowState.POP_UP.toString() %>"</i>). In fact, I have added a <i>parameter</i> (<span style="font-family: "courier new" , "courier" , monospace;">windowState</span>) into link address (URL) to tell system that I want to show the page in the popup style. Liferay automatically load the fit templates with this style (I will discuss this in the future posts which relate theme).<br />
<br /></div>
<div style="text-align: justify;">
Redeploy and click on the button. A window popup will appear, it only contains the content of portlet.</div>
<h3>
AUI Nav-item Tag</h3>
<div style="text-align: justify;">
The <a href="https://docs.liferay.com/portal/6.2/taglibs/aui/nav-item.html" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;"><aui:nav-item></span></a> tag is also <span style="font-family: "courier new" , "courier" , monospace;"><a href="https://docs.liferay.com/portal/6.2/taglibs/aui/button.html" target="_blank"><aui:button /></a> </span>tag. Following is the nessesary attributes:<br />
<ul>
<li><span style="background-color: white;"><i>href</i>: </span><span style="background-color: white;">its value is a URL. When you click on it,, browser will redirect to this link. If the value of useDialog is true (<i>useDialog="true"</i>), a window popup will show and include the redirected page.</span></li>
<li><span style="background-color: white;"><i>label</i>: the label of link</span></li>
<li><span style="background-color: white;"><i>title</i>: the title of window popup</span></li>
<li><span style="background-color: white;"><i>useDialog</i>: whether we open a window popup to load a new page?</span></li>
</ul>
</div>
<div style="text-align: justify;">
Following is the example which uses <span style="font-family: "courier new" , "courier" , monospace;"><aui:nav-item> </span>tag:</div>
<br />
<h3>
UI Icon Tag</h3>
<div style="text-align: justify;">
The last tag is <a href="https://docs.liferay.com/portal/6.2/taglibs/liferay-ui/icon.html" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;"><liferay-ui:icon></span></a>, this tag is very useful and frequently used. For examples: I have used this tag in post named "<a href="http://chingovan.blogspot.com/2016/04/actions-on-a-row-in-search-container.html" target="_blank">Liferay: Actions on a row in Search Container</a>". Besides, we can use it in many situations. The frequency attributes are:<br />
<ul>
<li><span style="background-color: white;"><i>label</i>: whether show the label of link?</span></li>
<li><span style="background-color: white;"><i>message</i>: the label of link and the title of window popup</span></li>
<li><span style="background-color: white;"><i>url</i>: its value is a URL, browser will navigate to the corresponding page when you click on it</span></li>
<li><span style="background-color: white;"><i>useDialog</i>: whether we open a window popup</span></li>
</ul>
</div>
<div style="text-align: justify;">
Similar to two above tags, <a href="https://docs.liferay.com/portal/6.2/taglibs/liferay-ui/icon.html" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;"><liferay-ui:icon></span></a> tag allows opening a window popup as well. Let see the following example:</div>
<br />
<h2>
Using Alloy UI</h2>
<div>
(to be continued)<br />
Your source code is <a href="https://github.com/programmerit/liferay-tutorial/tree/master/Popup-portlet" target="_blank">here</a></div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0tag:blogger.com,1999:blog-324075707410296810.post-42506060667311692532016-05-26T00:27:00.000+07:002017-02-06T10:58:08.886+07:00Liferay 6.2: Cửa sổ popup<div style="text-align: justify;">
<a href="http://chingovan.blogspot.com/2016/05/liferay-window-popup.html"><b>English version</b></a></div>
<div style="text-align: justify;">
<a href="https://4.bp.blogspot.com/-eDpCi1SgDTU/V0fRb9oO4NI/AAAAAAAAB9Q/vi2YSAARn4c9Hj1q3ZGt-VWO_k1SmBB1QCLcB/s1600/window%2Bpopup.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://4.bp.blogspot.com/-eDpCi1SgDTU/V0fRb9oO4NI/AAAAAAAAB9Q/vi2YSAARn4c9Hj1q3ZGt-VWO_k1SmBB1QCLcB/s1600/window%2Bpopup.png" /></a><br />
<br />
Cửa sổ popup là một tiện ích được tích hợp sẵn trong <i>Liferay</i>. Bản thân <i>Liferay</i> cũng sử dụng tiện ích này ở nhiều nơi. Ví dụ như trong giao diện phân quyền sử dụng (bạn click vào đường link phân quyền sẽ có một cửa sổ được bật nên để bạn chọn các quyền phù hợp), cấu hình portlet (bạn click vào các đường link cấu hình ở phía trên bên phải của mỗi portlet, sẽ có các cửa sổ được mở lên để bạn thay đổi cấu hình, giao diện, ... của portlet).</div>
<div style="text-align: justify;">
Nói chung, cửa sổ popup rất hữu dụng trong quá trình thiết kế giao diện ứng dụng. Vậy, làm thế nào có thể sử dụng được nó. Bài viết này sẽ giúp bạn.</div>
<br />
<a name='more'></a><br />
<div style="text-align: justify;">
Bài viết này được thử nghiệm với phiên bản Liferay 6.2. Với phiên bản này, <i>Liferay</i> đơn giản hóa việc gọi cửa sổ popup hơn các phiên bản trước đó. Ở đây, mình sẽ hướng dẫn hai cách mở cửa số popup. Đó là sử dụng các tiện ích có sẵn của <i>Liferay</i> và sử dụng <i>AUI</i> (<i>Alloy UI</i>) để mở cửa sổ popup.</div>
<h2>
Các tiện ích có sẵn</h2>
<div style="text-align: justify;">
<i>Liferay</i> cung cấp sẵn cho chúng ta ba thẻ <span style="font-family: "courier new" , "courier" , monospace;"><aui:button /></span>, <span style="font-family: "courier new" , "courier" , monospace;"><aui:nav-item /></span> và <span style="font-family: "courier new" , "courier" , monospace;"><liferay-ui:icon /></span>, chúng có tác dụng mở một cửa sổ popup mới khi chúng ta đặt giá trị của thuộc tính <span style="font-family: "courier new" , "courier" , monospace;">useDialog="true"</span>. Bây giờ, chúng ta sẽ đi xem xét từng thẻ và cách thức hoạt động của chúng.</div>
<div style="text-align: justify;">
Giả sử, ta có tệp <i>view.jsp</i> sẽ chứa ba thẻ trên và tệp <i>detail.jsp</i> sẽ được hiển thị trong cửa sổ popup khi ta gọi nó. Sau đây là nội dung của tệp <i>detail.jsp</i>:</div>
<script src="https://gist.github.com/programmerit/d17ea0b2a7216df426a9fdf65f68918f.js"></script><br />
<div style="text-align: justify;">
Có thể thấy rằng, nội dung của tệp <i>detail.jsp</i> rất đơn giản. Ta chỉ lấy từ <i>request</i> với khóa là <i>parameter</i> (dòng 3) và in lại nội dung của giá trị đó lên màn hình (dòng 6) mà thôi.</div>
<h3>
Thẻ AUI Button</h3>
<div style="text-align: justify;">
Thẻ <a href="https://docs.liferay.com/portal/6.2/taglibs/aui/button.html" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;"><aui:button /></span></a> là một thẻ hữu ích để tạo ra các button đẹp mắt. Sau đây là một vài thuộc tính mà ta cần quan tâm:</div>
<div style="text-align: justify;">
<ul>
<li><span style="background-color: white;"><i>href</i>: giá trị là một URL. Khi bạn click vào button, browser sẽ chuyển theo URL tương ứng. Nếu thuộc tính <i>userDialog="true"</i> thì sẽ mở một popup và trang được chuyển hướng đến sẽ nằm trong nó.</span></li>
<li><span style="background-color: white;"><i>icon</i>: hiển thị một icon trong nó.</span></li>
<li><span style="background-color: white;"><i>name</i>: tên button</span></li>
<li><span style="background-color: white;"><i>type</i>: loại button, các giá trị có thể chấp nhận là:</span> button, submit, cancel and reset.</li>
<li><span style="background-color: white;"><i>useDialog</i>: Liệu có mở một cửa sổ popup để tải trang mới hay không?</span></li>
<li><span style="background-color: white;"><i>value</i>: nhãn của button, đồng thời cũng là tiêu đề của popup.</span></li>
</ul>
</div>
<div style="text-align: justify;">
Bạn hãy xem ví dụ sau:</div>
<script src="https://gist.github.com/programmerit/7bfec069f80d963614a7560e8b78da78.js"></script><br />
<div style="text-align: justify;">
Bây giờ, hãy chạy thử portlet vừa tạo. Bạn sẽ thấy một nút bấm và click vào đó. Một cửa sổ popup sẽ hiện ra, bạn sẽ thấy rằng cửa sổ này giống hết cửa sổ phía dưới. Tuy nhiên, chỉ có phần màn hình trong portlet của chúng ta là khác. Vì portlet ở cửa sổ cha (cửa sổ chứa button) đang hiển thị tệp <i>view.jsp</i> còn cửa sổ popup thì hiển thị tệp <i>detail.jsp</i>.<br />
Nếu để cửa sổ popup chứa cả trang web như vậy thì rất khó theo dõi. Ta chỉ cần cửa sổ popup chứa nội dung của portlet mà thôi. Đoạn code sau đây sẽ giải quyết được yêu cầu trên.</div>
<script src="https://gist.github.com/programmerit/5850b1ae341acad7b9bca7158b1a7aa4.js"></script><br />
<div style="text-align: justify;">
So sánh hai đoạn code, ta sẽ thấy khác biệt nằm ở dòng số 4. Mình có thêm thuộc tính <i>windowState</i> (<i>windowState="<%= LiferayWindowState.POP_UP.toString() %>"</i>). Thực tế, là mình thêm một tham số (<i>windowState</i>) vào URL để nói cho hệ thống biết mình sẽ chuyển tới một màn hình mở được mở theo kiểu <i>POP_UP</i>. Hệ thống sẽ tự động tải các mẫu để phù hợp với yêu cầu (phần này sẽ được thảo luận trong các bài liên quan đến theme).</div>
<div style="text-align: justify;">
Bây giờ, chạy lại chương trình và click vào nút bấm trên màn hình, một cửa sổ popup hiện ra và bạn sẽ thấy chỉ có nội dung của portlet được hiển thị trong cửa sổ này mà thôi.</div>
<h3>
Thẻ AUI Nav-item</h3>
<div style="text-align: justify;">
Thẻ <a href="https://docs.liferay.com/portal/6.2/taglibs/aui/nav-item.html" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;"><aui:nav-item></span></a> cũng tương tự như thẻ <a href="https://docs.liferay.com/portal/6.2/taglibs/aui/button.html" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;"><aui:button /></span></a>. Sau đây là một số thuộc tính mà ta cần quan tâm:<br />
<ul>
<li><span style="background-color: white;"><i>href</i>: </span><span style="background-color: white;">giá trị là một URL. Khi bạn click vào đường link , browser sẽ chuyển theo URL tương ứng. Nếu thuộc tính <i>userDialog="true"</i> thì sẽ mở một popup và trang được chuyển hướng đến sẽ nằm trong nó.</span></li>
<li><span style="background-color: white;"><i>label</i>: nhãn của lên kết.</span></li>
<li><span style="background-color: white;"><i>title</i>: Tiêu đề của popup</span></li>
<li><span style="background-color: white;"><i>useDialog</i>: Liệu có mở cửa sổ popup để tải trang mới hay không?</span></li>
</ul>
</div>
<div style="text-align: justify;">
Sau đây là ví dụ sử dụng thẻ <span style="font-family: "courier new" , "courier" , monospace;"><aui:nav-item></span>:</div>
<script src="https://gist.github.com/programmerit/2cdd207c4ed3a1f85b3749f2d022d06d.js"></script><br />
<h3>
The UI Icon</h3>
<div style="text-align: justify;">
Thẻ cuối cùng mà ta quan tâm là <a href="https://docs.liferay.com/portal/6.2/taglibs/liferay-ui/icon.html" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;"><liferay-ui:icon></span></a>, thẻ này rất hữu ích và hay được sử dụng. Ví dụ, trong bài viết "<a href="https://chingovan.blogspot.com/2016/03/liferay-thao-tac-tren-tung-dong-cua.html" target="_blank">Liferay: Thao tác trên từng dòng của search-container</a>", mình đã sử dụng thẻ này để tạo ra các liên kết rất đẹp mắt. Bên cạnh đó, nó cũng còn có rất nhiều các tiện ích khác để chúng ta khai thác. Một số thuộc tính hay dùng của thẻ là:<br />
<ul>
<li><span style="background-color: white;"><i>label</i>: Liệu có hiển thị nhãn của liên kết hay không?</span></li>
<li><span style="background-color: white;"><i>message</i>: Nhãn của liên kết và đồng thời là tiêu đề của popup</span></li>
<li><span style="background-color: white;"><i>url</i>: Giá trị là một URL, browser sẽ chuyển đến trang tương ứng khi bạn click vào thẻ này.</span></li>
<li><span style="background-color: white;"><i>useDialog</i>: Liệu có mở trang mới trong một cửa sổ popup hay không.</span></li>
</ul>
</div>
<div style="text-align: justify;">
Tương tự hai thẻ trên, thẻ <a href="https://docs.liferay.com/portal/6.2/taglibs/liferay-ui/icon.html" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;"><liferay-ui:icon></span></a> cũng cho phép ta mở một cửa sổ popup, bạn hãy xem ví dụ sau:</div>
<script src="https://gist.github.com/programmerit/4a4d03c02e5409b5c3d3fa8857127d76.js"></script><br />
<h2>
Sử dụng Alloy UI</h2>
<div>
(còn nữa)<br />
Bạn có thể tải mã nguồn tại <a href="https://github.com/programmerit/liferay-tutorial/tree/master/Popup-portlet" target="_blank">đây</a></div>
ChiNV's Bloghttp://www.blogger.com/profile/15024318526607790352noreply@blogger.com0