Many of you will probably not agree with me, but if you read all this post, you will probably agree that commenting your source code is a sign that your code sucks. Real men write code that other people can read!
But keep in mind that I’m talking about comments that tell what the code do, and not documentation like “Javadoc”.
For example, if you ware not a Java programmer, or even being a java programmer, can you quickly tell what the code bellow do?
1 2 3 4 5 6 7 8 9 10 | public String write(StringBuilder fle, StringBuffer con) { File f = new File(fle.toString()); FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr); String lin; while((lin=br.readLine())!=null){ con.append(lin).append("\n"); } return con.toString(); } |
Hard one? And this is a very simple code, but we can make it a little better …
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public String read(StringBuilder fle, StringBuffer con) { //Opens the file with the name container in the fle parameter File f = new File(fle.toString()); //Create a file reader, then a buffered reader to make our work easier FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr); String lin; //Read each line of the file until it is null while((lin=br.readLine())!=null){ //Put the content read into the buffer pointed by the parameter "con" con.append(lin).append("\n"); } //The caller already have the content, because he created the buffer, but I'll return the string anyway return con.toString(); } |
Easier, right? Now you can read the comments in the code, and understand it, but the code still crappy.
This is the kind of comment I’m talking about. Commenting your code is a sign that you are a very bad programmer (with little exceptions, in very complex algorithms that you can not break in smaller pieces of code).
When you do this, you are doing a “hack” to hide from others your own incompetence. This mean that you cannot write a clearer code. One that other programmers can read.
Here we have a little solution for the code above, a refactoring of that crappy method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public String readFileContents(File fileToRead) { boolean canReadFile = fileToRead.exists(); if(!canReadFile) return ""; StringBuilder buffer = new StringBuilder(); BufferedReader readerForFile = openBufferedReaderForFile(fileToRead); readFileContetIntoBuffer(buffer,readerForFile); closeFileReader(readerForFile); return buffer.toString(); } private BufferedReader openBufferedReaderForFile(File fileToRead){ return new BufferedReader(new FileReader(fileToRead)); } private void readFileContetIntoBuffer(buffer,readerForFile){ String line; while((line=readerForFile.readLine())!=null){ buffer.append(line).append("\n"); } } private void closeFileReader(readerForFile){ readerForFile.close(); } |
Now if you pay attention to the method name “readFileContents”, you can already know what it does, besides that, the method code is almost legible, as you can see bellow:
if not can read file, return null
open Buffered Reader For File: fileToRead
read File Contet Into Buffer: buffer, readerForFile
close File Reader: readerForFile
return buffer.toString();
What I’m telling is that if you can read english, you can read the code, and almost every programmer in the planet knows how to read english, right?
I have even seen people doing worst things, like the snippet bellow:
1 2 3 4 5 | public String write(StringBuilder fle, StringBuffer con) { File f = new File(fle.toString()); FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr); String lin; while((lin=br.readLine())!=null){ con.append(lin).append("\n"); } return con.toString(); } |
It has less lines of code than my version, but you will have a hard time trying to read a program wrote by the monster that wrote the snippet above.
Of course the example I used was a simple one, and I know that writing legible code needs practice …
But I’ll let you a proposal, not really a proposal, an exercise …
I’ll let a code example bellow, and you will try to make it more readable. And I’ll post my solution in one or two days.
If you want to post the refactored code in the comments, just use the tags <pre lang=”java” line=”1″> … </pre>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | package blog; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class VeryBadlyNamedFile { private static final char[] asdfg = new char[] {'I', ' ', 'c', 'a', 'n', ' ', 'd', 'o', ' ', 'v', 'e', 'r', 'y', ' ', 'u', 'g', 'l', 'y', ' ', 'c', 'o', 'd', 'e'}; private String an; private BufferedReader rfsdw; private FileReader temp; public VeryBadlyNamedFile(String an, BufferedReader rfsdw, FileReader temp) { super(); this.an = an; this.rfsdw = rfsdw; this.temp = temp; } public void doIt() throws IOException { ctfiidne(); startDoing(); try { canIDoAnyThing(); } catch (RuntimeException yicdet) { nowReallyDoIt(); } } private void nowReallyDoIt() { firstDoTheOtherThing(); reallyDoItInternal(); } private void firstDoTheOtherThing() { rfsdw = new BufferedReader(temp); } private void reallyDoItInternal() { while (true) { try { imDoingIt(); } catch (Exception e) { break; } } } private void imDoingIt() throws Exception { String s = rfsdw.readLine(); if (s == null) throw new Exception("hahaha, I bet you did not understood the code"); System.out.println(s); } private void ctfiidne() throws IOException { File a = new File(an); if (!a.exists()) { FileWriter wrfedsd = new FileWriter(a); wrfedsd.write(asdfg); wrfedsd.close(); } } private void canIDoAnyThing() { if (new File(an).exists() && new File(an).canRead() && new File(an).canWrite()) throw new RuntimeException(); } private void startDoing() throws FileNotFoundException { File f = new File(an); temp = new FileReader(f); } } |
And now a “main” file to exebute the trash above.
1 2 3 4 5 6 7 8 9 | package blog; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { new VeryBadlyNamedFile("c:\\anyFile.any",null,null).doIt(); } } |
The examples in this post are using Java, but the concept apply to any language, if you are programming and using comments to explain your code, you are a terrible programmer. Just pray for god’s help, and beg him that the next one to read the crap you are creating is not a serial killer that knows where you live.
PS.: I really do not think that you should not comment your code, but if your code is not understandable without the comments, you do not know how to write programs yet.
PS2.: I think I need better examples.
PS3.: the secret to write good code is to write it as if the next one that will read your code is a serial killer that knows where you live. (I do not remember who wrote this sentence in the first place, I’ll research and post it here)
If you enjoyed this post, make sure you subscribe to my RSS feed!
Yesterday I finished the third and last part of the SCEA 5 Beta exam …
The only bad news is that the results will only be available after February 15th
One good thing, is that this is the only certification I took until now that worth something!
The first objective test is very easy if you already work with system architecture and know at least a little about each Java EE technology …
The second part is not hard too, you have to design a system for a given problem, the only hard part on the Beta was to do it within 2 weeks, and for me those ware two full weeks, I was moving from one city to another, and already had some other things to do, at the end I could work on the project for only 20 hours (it was specified for between 40 and 60 hours), I hope I did it well enough …
The third and last part, is basically to prove that it was really you that created the project …
Before going to the test place, make sure you read all that you send with your project, write down every decision you make and why you made it, this two little tips will help you a lot in the final part of this certification …
Well, not I’ll wait untill February 15th to know if I cleared the exam ![]()
Good luck for me and for every one that read to here
If you enjoyed this post, make sure you subscribe to my RSS feed!
Tags: certification, java, javaee, scea, test
Working with Flex and Java as backend, I decided that the best choice for the project I’m working now is to simply post XML from Flex and get XML back from Java (Flex works really well with XML).
But there is a problem with that, creating a XML with one or two values is easy, but if you need to serialize a very large object tree you just got yourself a lot of work.
So I’ve wrote this very simple Flex XML Serializer, and decided to post it here, because some one might need some thing similar
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package teste { import mx.utils.ObjectUtil; public class Utils { public static function objectToXml(obj : Object, name : String) : XML{ var result : XML; var info:Object = ObjectUtil.getClassInfo(obj); if(name==null) name = info.name; result = new XML("<" + name + "></"+ name + ">"); for each (var qn : QName in info.properties){ var val : Object = obj[qn.toString()]; if(ObjectUtil.isSimple(val)) result[qn.toString()] = val; else result.appendChild(objectToXml(val,qn.toString())); } return result; } } } |
To use is is really simple:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="appInit()"> <mx:Script> <![CDATA[ import teste.Utils; import mx.utils.ObjectUtil; import mx.controls.Alert; public function appInit() : void { var obj : Object = {name:'teste',address:{street:'rua',number:20}}; Alert.show(Utils.objectToXml(obj,'teste').toXMLString()); } ]]> </mx:Script> </mx:Application> |
The generated XML will look like this:
1 2 3 4 5 6 7 | <teste> <address> <number>20</number> <street>rua</street> </address> <name>teste</name> </teste> |
This might not be the best way to work, but I liked this solution for the problem I had
One thing I changed from this code to the code that is in production today is that I’m using XML attributes now, and as you can see below the change was big from the first example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package teste { import mx.utils.ObjectUtil; public class Utils { public static function objectToXml(obj : Object, name : String) : XML{ var result : XML; var info:Object = ObjectUtil.getClassInfo(obj); if(name==null) name = info.name; result = new XML("<" + name + "></"+ name + ">"); for each (var qn : QName in info.properties){ var val : Object = obj[qn.toString()]; if(ObjectUtil.isSimple(val)) result['@' + qn.toString()] = val; else result.appendChild(objectToXml(val,qn.toString())); } return result; } } } |
Could not find the difference? the only change was the “‘@’ +” at line 16, and I was using attributes for the values ![]()
So, what do you think about this solution?
The next step is to write a deserializer and we’ll make XML rock the Flex world (just kidding
)
If you enjoyed this post, make sure you subscribe to my RSS feed!
Tags: actionscript, flex, howto, tip
I just have be notified that I passed the first part of the beta SCEA 5 certification
Dear Rodrigo U Jardim,
Congratulations! You passed the SCEA Part 1 Beta exam. Passing score is 57%, your score is 77%. We will be sending a separate email containing your Part 2 assignment (which is a PDF file), complete with instructions, within the next 8 hours. If you do not receive the second email containing the assignment within the next 8 hours, please notify us at …… Thank you.
Sincerely,
Now I`ll wait for the PDF with the second part of the assignment and run to deliver within one month what most of the people I know took at least six months to do
PS.: This post was just to clean up the dust from the blog, I`ve been involved in lots of projects lately but I have about 5 post drafts to publish here, I’ll try to start posting at least one per day as I used to do
If you enjoyed this post, make sure you subscribe to my RSS feed!
Tags: certification, scea
Probably you have already heard about Adobe Flex.
Flex is a SDK to develop Rich Internet Applications that will run within the Flash Player, and Flash Player is present in almost all browser and all desktop platforms today.
Flex applications generate an SWF file and this file will communicate with a back end server, this backend server can be a Flash Lifecycle Server, but you can use java and open source for it too.
This two/tree examples work without any paid software, you will need only:
Flex SDK – that is free, and the version 3 will be open source.
A servlet container – I’m using Tomcat
– For the first example that is all
And for the other two examples you’ll need to download
OpenAMF – A flex remoting implementation in Java
RemoteObjectAMF0 – an implementation of the RemoteObject tag that supports the version 0 of AMF protocol (OpenAMF does not supports the version 3 yet)
So, lets start, create a java web project with your favorite IDE, and create a Servlet named TestServlet, I’ll use jaxb to render XML from the servlet, but you can use anything else, if you want to use jaxb too, the schema I created for this example is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.com/example" xmlns:tns="http://www.example.com/example" elementFormDefault="qualified"> <element name="TestSVo" type="tns:TestList"></element> <complexType name="TestSVoType"> <attribute name="id" type="int"></attribute> <attribute name="name" type="string"></attribute> <attribute name="other" type="string"></attribute> </complexType> <complexType name="TestList"> <sequence minOccurs="1" maxOccurs="unbounded"> <element name="all" type="tns:TestSVoType"></element> </sequence> </complexType> </schema> |
This schema is for a XML like the following:
1 2 3 4 5 | <TestSVo> <all id="0" name="foo" other="bar"/> <all id="1" name="foo1" other="bar1"/> ... </TestSVo> |
Generate the stubs for the XML generation using JaxB or use any other tool to generate a XML with this schema.
Like any other Java application, I’ll start with my web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd "> <servlet> <servlet-name>testServlet</servlet-name> <servlet-class>....servlet.TestServlet</servlet-class> </servlet> <servlet> <servlet-name>AdvancedGateway</servlet-name> <servlet-class>org.openamf.AdvancedGateway</servlet-class> <init-param> <description> Location of the OpenAMF config file. </description> <param-name>OPENAMF_CONFIG</param-name> <param-value>/WEB-INF/openamf-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>AdvancedGateway</servlet-name> <url-pattern>/gateway2</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>testServlet</servlet-name> <url-pattern>/TestServlet</url-pattern> </servlet-mapping> <session-config> <session-timeout>30</session-timeout> </session-config> <welcome-file-list> <welcome-file>teste.html</welcome-file> </welcome-file-list> </web-app> |
for the first example, the only needed servlet is the test servlet, for the other examples the AdvancedGateway servlet is used …
Now write the following code in the servlet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; /** * Servlet implementation class for Servlet: TestServlet * */ public class TestServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet { static final long serialVersionUID = 1L; private JAXBContext jc; private Marshaller marc; private Unmarshaller unmarc; private ObjectFactory factory; public TestServlet() { super(); factory = new ObjectFactory(); try { jc = JAXBContext.newInstance("....servlet"); marc = jc.createMarshaller(); unmarc = jc.createUnmarshaller(); } catch (JAXBException e) { e.printStackTrace(); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { float v = Float.parseFloat(request.getParameter("v")); float v1 = Float.parseFloat(request.getParameter("v1")); response.getWriter().print(v * v1); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("List called"); TestList res = new TestList(); for(int i=0;i<40;i++){ res.getAll().add(new TestSVoType(i,"Téste ã " + i)); } JAXBElement<TestList> elem = factory.createTestSVo(res); System.out.println(elem.toString()); try { marc.marshal(elem, response.getWriter()); } catch (JAXBException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
the doGet method of this servlet is a calculator, it just multiply the two parameters and prints out the result.
The doPost prints a XML like the one above, automatically generated using the JAXB API.
Now the flex part:
Create a file named xmltest.mxml in the home folder of your webapp with the following content.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" backgroundColor="#F4F4F4"> <mx:HTTPService id="calc" method="GET" url="http://localhost:8080/teste/TestServlet" > </mx:HTTPService> <mx:HTTPService id="list" method="POST" url="http://localhost:8080/teste/TestServlet" > </mx:HTTPService> <mx:Script> <![CDATA[ import mx.controls.DataGrid; import mx.controls.Alert; ]]> </mx:Script> <mx:Panel width="80%" height="531" title="Teste"> <mx:Label width="100%" color="blue" text="Type two numbers and press calculate." /> <mx:TextInput id="v1" text="3" /> <mx:TextInput id="v2" text="5" /> <mx:Label id="lbl" text="{calc.lastResult}"/> <mx:Button label="Calculate" click="calc.send({v:v1.text,v1:v2.text});" /> <mx:DataGrid dataProvider="{list.lastResult.TestSVo.all}" width="100%" height="195" change="Alert.show(DataGrid(event.currentTarget).selectedItem.other)"> <mx:columns> <mx:DataGridColumn headerText="Id" dataField="id" /> <mx:DataGridColumn headerText="Nome" dataField="name" /> </mx:columns> </mx:DataGrid> <mx:Button label="List" click="list.send({v:v1.text,v1:v2.text})" /> </mx:Panel> </mx:Application> |
The interface with the java code is in the two mx:HTTPService lines, the first one invokes the URL with a GET request, calling the calculator method
Then the first button (Calculate) is activated, the fist service is called [calc.send(parameters)] and the text on the first label will be updated because of the value binding on the last result …
When the seccond button (List) fires a post [list.send(params)] and the data grid will process the returned XML and display the lines …
That is all!
Gotcha: if you call list.send without parameters the HTTPService tag fires a GET method, this is the reason for the unused parameters there, at least with Flex 2 this happened every time
This aproach is very simple and does not need much code from any side, but it does not looks like OOP very much …
And flex has a great remoting support, so what do you think agout creating a POJO in Java and use it as a “remote object” from your flex code?
Like the idea?
So let’s create the TestService bellow with the same two operations (calc and list)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import java.util.ArrayList; import java.util.List; public class TestService { public float calc(float v, float v1){ System.out.format("%f X %f\n", v, v1); return v * v1; } public List<TestVo> list(){ System.out.println("List called"); ArrayList<TestVo> res = new ArrayList<TestVo>(); for(int i=0;i<40;i++){ res.add(new TestVo(i,"Testé ã " + i)); } return res; } } |
And now, we do not need all that XML stuf, a plain old VO will do the JOB …
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public class TestVo { private String name; private int id; public TestVo(int id, String name) { this.id = id; this.name = name; } public String getName() { return name; } public int getId() { return id; } public void setName(String name) { this.name = name; } public void setId(int id) { this.id = id; } } |
To use this service from Flash, we need to configure the OpenAMF framework using the file WEB-INF/openamf-config.xml, the name and location of this file was configured as a parameter to the AdvancedGateway in web.xml file …
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <?xml version="1.0" encoding="UTF-8"?> <config> <amf-serializer> <force-lower-case-keys>false</force-lower-case-keys> </amf-serializer> <invoker> <name>Java</name> <class>org.openamf.invoker.JavaServiceInvoker</class> </invoker> <custom-class-mapping> <java-class>....TestVo</java-class> <custom-class>TestVo</custom-class> </custom-class-mapping> <service> <name>TestService</name> <service-location>....TestService</service-location> <invoker-ref>Java</invoker-ref> <method> <name>calc</name> <parameter> <type>*</type> </parameter> </method> <method> <name>list</name> <parameter> <type>*</type> </parameter> </method> </service> </config> |
I have removed almost all the code from this file
There is the Serializer/Deserializer configured, the only registered invoker is the Java invoker, there is one custom-class-mapping for the VO and a Service definition for the service.
If you do not want to declare all methods for each service, you can use * as the name, and it will match any method …
The OpenAMF frameworks has many more configuration options, you can call EJBs, WebServices, any java class, JMX beans and Spring Beans from your flex code without any problem …
We can not use the standard mx:RemoteObject to call this service, because OpenAMF does not support AMF3, only AMF0 and I do not know how to configure the AMF protocol version for the mx:RemoteObject tag …
So the first example, uses only a NetConnection instance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" backgroundColor="#F4F4F4" initialize="init()"> <mx:Script> <![CDATA[ import mx.controls.Alert; import mx.rpc.events.ResultEvent; import mx.collections.ArrayCollection; [Bindable] public var lista:ArrayCollection; public var nc : NetConnection; function init() : void{ nc = new NetConnection(); nc.objectEncoding = ObjectEncoding.AMF3; nc.addEventListener(NetStatusEvent.NET_STATUS,netStatus); nc.connect("http://localhost:8080/teste/gateway2"); } function netStatus(event : NetStatusEvent) : void { Alert.show(event.info.code); } function onRetornaLista(event):void{ var e : Array = event; lista = new ArrayCollection(e); } function onRetornaCalc(event):void{ lbl.text = String(event); } function calc(v1 : Number, v2 : Number) : void { var r : Responder = new Responder(onRetornaCalc); nc.call("TestService.calc",r,v1,v2); } function list() : void{ var r : Responder = new Responder(onRetornaLista); nc.call("TestService.list",r); } ]]> </mx:Script> <mx:Panel width="80%" height="531" title="Teste"> <mx:Label width="100%" color="blue" text="Type two numbers and press calculate." /> <mx:TextInput id="v1" text="3" /> <mx:TextInput id="v2" text="5" /> <mx:Label id="lbl" /> <mx:Button label="Calculate" click="calc(Number(v1.text),Number(v2.text))" /> <mx:DataGrid dataProvider="{lista}" width="100%" height="195"> <mx:columns> <mx:DataGridColumn headerText="Id" dataField="id" /> <mx:DataGridColumn headerText="Nome" dataField="name" /> </mx:columns> </mx:DataGrid> <mx:Button label="List" click="list()" /> </mx:Panel> </mx:Application> |
the init method just initialized the NetConnection object (nc).
When the first button is called (Calculate) the calc method is called, in this method we call the TestService.calc method in the server, using the NetConnection object.
The new thing here is that we need the Responders to process the result from the server call …
I think this approach is better than the last one, but still not looking very good for me.
So I looked a little around and found the RemoteObjectAMF0 library …
It is a little ActionScript library, that enables you to use tags to call your service from flex using the AMF0 protocol like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:renaun="com.renaun.rpc.*" layout="vertical" backgroundColor="#F4F4F4"> <mx:Script> <![CDATA[ import mx.controls.Alert; import mx.rpc.events.ResultEvent; import mx.collections.ArrayCollection; [Bindable] public var lista:ArrayCollection; NetConnection.defaultObjectEncoding = ObjectEncoding.AMF0; function onRetornaLista(event:ResultEvent):void{ lista = ArrayCollection(event.result); } function onRetornaCalc(event:ResultEvent):void{ lbl.text = String(event.result); } ]]> </mx:Script> <renaun:RemoteObjectAMF0 endpoint="http://localhost:8080/teste/gateway2" id="TST" source="....TestService" showBusyCursor="true" makeObjectsBindable="true" fault="Alert.show(String(event.fault)), 'Error'"> <renaun:methods> <renaun:method name="list" result="onRetornaLista(event)"></renaun:method> <renaun:method name="calc" result="onRetornaCalc(event)"></renaun:method> </renaun:methods> </renaun:RemoteObjectAMF0> <mx:Panel width="80%" height="531" title="Teste"> <mx:Label width="100%" color="blue" text="Type two numbers and press calculate." /> <mx:TextInput id="v1" text="3" /> <mx:TextInput id="v2" text="5" /> <mx:Label id="lbl" /> <mx:Button label="Calculate" click="TST.calc(Number(v1.text),Number(v2.text))" /> <mx:DataGrid dataProvider="{lista}" width="100%" height="195"> <mx:columns> <mx:DataGridColumn headerText="Id" dataField="id" /> <mx:DataGridColumn headerText="Nome" dataField="name" /> </mx:columns> </mx:DataGrid> <mx:Button label="List" click="TST.list()" /> </mx:Panel> </mx:Application> |
Still not perfect, since I did not liked the idea of using the full file name to call a service, I think my final solution will be to create an dynamic proxy for the services, but this last one looks good enought for me
I hope this post helps some one, I spend two days looking for solutions for this problem, because the current project does not have enought cash for a Life Cycle Server license …
PS.: to compile the mxml files you can use the mxmlc command from the free flex SDK
PS2.: you need to fix the URLs to the full path for your context, and your flex app must be downloaded from the same domain
PS3.: the é and ã letters are not encoding problems, I put it there to test if there would be any encoding problems and there were none
If you enjoyed this post, make sure you subscribe to my RSS feed!
Following the very simple example line, as in the “login example with Rails“, now we’ll see a very simple login example with Java Server Faces.
I’m writing this sequence of short howto posts because in the forums I read one of the recurrent question from the beginners is “how to implement a login with X”, so, let’s go to the JSF login example.
Of course there are many ways to implement a login in a JSF aplication, you can use JAAS, you can use a Servlet Filter, but this one I think is one of the best approaches, of course, in a real application, I usually combine it with some AOP and annotations, but AOP and annotations are out of the scope of this tutorial …
First, write a web.xml for your application, with the faces servlet in it …
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> <error-page> <exception-type>java.lang.SecurityException</exception-type> <location>/login.jsf</location> </error-page> </web-app>
In this web.xml we are using the servlets 2.5 specification, and I’m using JSF 1.2, for this example you can use other versions without problem.
The servlet container is configured to show a login page if at any time the application throws a java.lang.SecurityException, this is a important point for the example.
And now a backing bean for the application, here is where we’ll validate the user’s login, in this example there is no need for database access, but in a real application you’ll search this data in your user repository (database, ldap, …)
package br.com.urubatan.jsfjpasec; public class Login { private boolean loginOk; private String userName; private String password; public boolean isLoginOk() { return loginOk; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String validateLogin(){ if(userName!=null && password!=null && !userName.equalsIgnoreCase(password)){ loginOk = true; return "secpage"; }else return "login"; } }
It is a very simple backing bean, with only 3 properties:
name and password for the user, and a property telling the application that a user has already logged in.
probably this last property will be replaced by a list of groups filled by the validateLogin method …
Now a bean with some static data for the example:
package br.com.urubatan.jsfjpasec; import java.util.List; import java.util.ArrayList; public class SomeData { private List<String> data = new ArrayList<String>(); private List<String> securedData = new ArrayList<String>(); private boolean loginOk; public SomeData() { for(int i=0;i<10;i++){ data.add("Simple data " + i); securedData.add("Secure data " + i); } } public void setLoginOk(boolean loginOk) { this.loginOk = loginOk; } public List<String> getSecuredData() { if(!loginOk) throw new SecurityException(); return securedData; } public List<String> getData() { return data; } }
This one has only the getters for two properties, and in the secureData property, if there is no logged in user, the application throws a java.lang.SecurityException, this will redirect the user to the login page.
Now some XML tricks in the faces-config.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <?xml version='1.0' encoding='UTF-8'?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd" version="1.2"> <managed-bean> <managed-bean-name>login</managed-bean-name> <managed-bean-class>br.com.urubatan.jsfjpasec.Login</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>mdata</managed-bean-name> <managed-bean-class>br.com.urubatan.jsfjpasec.SomeData</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>loginOk</property-name> <property-class>java.lang.Boolean</property-class> <value>#{login.loginOk}</value> </managed-property> </managed-bean> <navigation-rule> <from-view-id>/login.jsp</from-view-id> <navigation-case> <from-outcome>login</from-outcome> <to-view-id>/login.jsp</to-view-id> <redirect/> </navigation-case> <navigation-case> <from-outcome>secpage</from-outcome> <to-view-id>/secureView.jsp</to-view-id> <redirect/> </navigation-case> </navigation-rule> </faces-config> |
In this file we have two navigation cases, if “login” is returned from a backing bean, it shows the login page, if “secpage” is returned, the secureView.jsp page is shown …
The first lines are used to declare the backing beans, pay attention to the line 15, where we are reading the property “loginOk” from the login bean.
The login bean is session scoped, and the mdata is request scoped.
With this written we have all the needed logic for this application, the only missing part is the “view”, or the JSP files …
So, let’s write them …
login.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <html> <head><title>System Login</title></head> <body> <f:view> <h:form> <h:panelGrid columns="2"> <h:outputLabel value="User Name" for="un"/> <h:inputText id="un" value="#{login.userName}"/> <h:outputLabel value="Password" for="pw"/> <h:inputText id="pw" value="#{login.password}"/> </h:panelGrid> <h:commandButton value="Login" action="#{login.validateLogin}"/> </h:form> </f:view> </body> </html>
This is only a simple JSF page with two fields and a commandButton …
dataView.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <html> <head><title>Unsecured Data Page</title></head> <body> <f:view> <h:dataTable value="#{mdata.data}" var="v"> <h:column> <f:facet name="header"> <h:outputText value="Data List"/> </f:facet> <h:outputText value="#{v}"/> </h:column> </h:dataTable> <h:panelGrid columns="3"> <h:outputLink value="dataView.jsf"> <h:outputText value="Data that every one can access"/> </h:outputLink> <h:outputLink value="secureView.jsf"> <h:outputText value="Data that you can view after login"/> </h:outputLink> <h:outputLink value="login.jsf"> <h:outputText value="Login"/> </h:outputLink> </h:panelGrid> </f:view> </body> </html>
This is simple page with a dataTable rendering the “data” property from “mdata” bean and two links.
secureView.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <html> <head><title>Secured Data Page</title></head> <body> <f:view> <h:dataTable value="#{mdata.securedData}" var="v"> <h:column> <f:facet name="header"> <h:outputText value="Data List"/> </f:facet> <h:outputText value="#{v}"/> </h:column> </h:dataTable> <h:panelGrid columns="3"> <h:outputLink value="dataView.jsf"> <h:outputText value="Data that every one can access"/> </h:outputLink> <h:outputLink value="secureView.jsf"> <h:outputText value="Data that you can view after login"/> </h:outputLink> <h:outputLink value="login.jsf"> <h:outputText value="Login"/> </h:outputLink> </h:panelGrid> </f:view> </body> </html>
And this is almost a copy from the previous page, but now reading the “secureData” property from “mdata” bean.
And just to avoid the “404″ error when running the application, an “index.jsp” with a link to “dataView.jsf”
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>Entry Page</title></head> <body>Entry Page, this could redirect to the JSF Index, but for now, click here: <a href="dataView.jsf">JSF Index</a> </body> </html>
That is all folks!
to run this application you need the following jar files: jstl.jar, jsf-api.jar e jsf-impl.jar
They can be found in the JSF-RI distribution, and are already present in containers with JSF support.
And now, some questions:
If you enjoyed this post, make sure you subscribe to my RSS feed!