DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

The Latest Coding Topics

article thumbnail
Business Logic in Domain Objects?
One of the design principles of any enterprise application which I found difficult to make up my mind about is Anemic domain models + Service Objects Vs. Fully featured domain objects. Most of my business application layer consists of many Service Objects (DAOs, Session Beans, BOs, Delegates, Facades etc) AND many POJO domain objects. Here the domain objects are nothing but data holders. There is no business logic in it. Arguably, this is an application of "separation of concerns", where business and/or data access logic is separated from data holders (in other words, Transfer Objects or Value Objects). In my opinion, this also improves the testability of the code. Whereas, there is a counter argument from another side (mostly the proponents of Domain- Driven Development (DDD)) that separating domain objects from its operations is nothing but going back to procedural style of programming. I don't know if I necessarily agree with that argument. Frameworks like Hibernate (even JPA) allows us to embed custom SQL's inside the domain model. In my mind that is a 'mixing' of concerns. Also, imagine a method called sell() inside a 'Book' object. Does a book know how to sell it? Should it know? Also, Book has an existence without a sell() functionality. In cases like this, I prefer the business logic to be separated in to respective Service Classes which results in methods like BookServiceImpl.sell(book) and BookServiceImpl.calculatePrice(book) rather than Book.sell() and Book.calculatePrice(). This is not to totally dismiss the other argument. If we are developing software for a rapidly changing business domain, from a maintainability perspective, DDD approach may sound attractive. For example, when the way company sell books change , the place it requires the code change is most likely the Book.sell() method; where as, if we are to change the service method, it might affect various other sub-systems in which the service methods are re-used. But when Book object requires another domain object (e.g: 'CreditCard' object) to do its selling, things become complicated. Yes, we can inject CreditCard object into Book object, but effectively we are introducing a level of coupling and reducing testability.
May 28, 2010
by Bino B. Manjasseril
· 42,938 Views · 2 Likes
article thumbnail
JMS Clustering by Example
It's amazing how the JBoss Team put together an easy way to do JMS Clustering, out of the box!!. I'll start with an easy example, creating a Queue named "MyClusteredQueue". In this example I'm using JBoss AS 5.1. and two computers connected on the same network, with these IP's: - Computer A: 192.168.0.143 - Computer B: 192.168.0.210 So, here are the steps: 1) Install the JBoss on both computers. We are going to use the "all" configuration for both computers. 2) We create our Queue on both servers. Go to $JBOSS_HOME/server/all/deploy/messaging/ and edit the destinations-service.xml file. Add the MyClusteredQueue before the last server tag. It looks like this: jboss.messaging:service=ServerPeer jboss.messaging:service=PostOffice true This is how you add a Queue to the JBoss, and the people how are familiar with this, the only new thing is to add the attribute "Clustered". This step must be set on both computers. At the end of the article you can find the files. 3) Write the MDB to consume the messages, and deploy it on the two computers. (I'm using an EJB 3 - MDB style). import java.net.InetAddress; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.ObjectMessage; import org.apache.log4j.Logger; /** * @author felipeg * */ @MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"), @ActivationConfigProperty(propertyName="destination", propertyValue="queue/MyClusteredQueue") }) public class JMSClusterClientHandler implements MessageListener { Logger log = Logger.getLogger(JMSClusterClientHandler.class); @Override public void onMessage(Message message) { try{ if (message instanceof ObjectMessage) { InetAddress addr = InetAddress.getLocalHost(); log.info("########## Processing Host: " + addr.getHostName() + " ##########" ); ObjectMessage objMessage = (ObjectMessage) message; Object obj = objMessage.getObject(); log.info("Object received:" + obj.toString()); } } catch (Exception e) { e.printStackTrace(); } } } 4) Start the jboss with the following options: Computer A: $ cd $JBOSS_HOME/bin $ ./run.sh -c all -b 192.168.0.143 -Djboss.messaging.ServerPeerID=1 Computer B: $ cd $JBOSS_HOME/bin $ ./run.sh -c all -b 192.168.0.210 -Djboss.messaging.ServerPeerID=2 It is necesary to give an ID to each server and this is accomplished with this directive: -Djboss.messaging.ServerPeerID When you start the jboss on computer A, you should see the logs (server.log) telling you that there is one node ready and listening, and once you start the jboss on computer B, on the log will appear the two nodes, the two IP's ready to consume messages. 5) Now it's time to send a Message to the Queue. To accomplish this it's necessary to change the connection factory to "ClusteredConnectionFactory" (JMSDispatcher.java - See the code below). Also on the jndi.properties (if you are using the default InitialContext) file it's necessary to add the two computers ip's separated by comma to the java.naming.provider.url property. (In my case a create a Properties variable and I set all the necessary properties, JMSDispatcher.java - see the code below). java.naming.provider.url=192.168.0.143:1099,192.168.0.210:1099 The client that I wrote is a web application, that consist in one index.jsp page, which contains a form that prompts you for the name of the queue, the type of messaging (Queue or Topic), the server ip and port, how many times it will send the message and the actual message to be sent; also the web application has a Servlet (JMSClusteredClient.java - see code below) that receives the postback and helper class (JMSDispatcher.java - see code below) that sends the message to the jboss servers. You can to deploy it in any computer. In my case I deployed it on the Computer A. And you can access it through this URL: http://192.168.0.143:8080/JMSWeb/ (just modify the IP where the client war was deployed). If you notice (on the index.jsp - code below) I've already put some default values that reflects the name of the Queue, and the IP's of my two computers. Now, If you increment the number of times that the message will be sent (maybe a 10) and fill out the message box, and click "Send" you should see on the two servers some of the messages being consumed by the MDB. Here are the Files to create the client: index.jsp JMS Clustered - Test Client Server: QueueTopic Times:Message: Servlet: JMSClusteredClient.java public class JMSClusteredClient extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response) */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); String topicqueue = request.getParameter("topicqueue"); String message = request.getParameter("message"); String server = request.getParameter("server"); String messageType = request.getParameter("messageType"); String times = request.getParameter("times"); int intTimes = Integer.parseInt(times); JMSDispatcher dispatcher = new JMSDispatcher(); dispatcher.setTopicQueueName(topicqueue); dispatcher.setServer(server); dispatcher.setMessageType(messageType); try { for(int count =1; count <= intTimes;count++){ dispatcher.sendMessage( count + " of " + times + " " + message); } out.println("Message [" + message + "] sent successfully to [" + topic + "] to the [" + server + "] server " + times + " times."); } catch (JMSException e) { e.printStackTrace(); out.println("Error:" + e.getMessage()); } catch (NamingException e) { out.println("Error:" + e.getMessage()); e.printStackTrace(); } finally{ out.close(); } } } A utility to send the messages: JMSDispatcher.java public class JMSDispatcher { /** * */ private static final long serialVersionUID = 7105145023422143880L; private static Logger log = Logger.getLogger(JMSDispatcher.class); private final String CONNECTION_FACTORY_CLUSTERED = "ClusteredConnectionFactory"; private final String CONNECTION_FACTORY = "ConnectionFactory"; private final String TOPIC = "TOPIC"; private final String QUEUE = "QUEUE"; private String topicQueueName; private String server; private String messageType; public void setTopicQueueName(String value){ this.topicQueueName = value; } public void setServer(String value){ this.server = value; } public void setMessageType(String value){ this.messageType = value; } public void sendMessage(Object objectMessage) throws JMSException, NamingException{ log.debug("##### Setting up a Queue/Topic Message: #####"); if (TOPIC.equals(messageType)){ sendTopicMessage(objectMessage); } else if (QUEUE.equals(messageType)){ sendQueueMessage(objectMessage); } log.debug("##### Publishing Message: Done #####"); } private void sendQueueMessage(Object objectMessage) throws JMSException, NamingException{ try{ InitialContext initialContext = getInitialContext(); QueueConnectionFactory qcf = (QueueConnectionFactory) initialContext.lookup(CONNECTION_FACTORY_CLUSTERED); QueueConnection queueConn = qcf.createQueueConnection(); Queue queue = (Queue) initialContext.lookup(topicQueueName); QueueSession queueSession = queueConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); queueConn.start(); QueueSender send = queueSession.createSender(queue); ObjectMessage om = queueSession.createObjectMessage((Serializable)objectMessage); setMessageProperties(om); log.debug("##### Publishing Message to a Queue: " + queueName + "#####"); send.send(om); send.close(); queueConn.stop(); queueSession.close(); queueConn.close(); }catch(MessageFormatException ex){ log.error("##### The MESSAGE is not Serializable ####"); throw ex; }catch(MessageNotWriteableException ex){ log.error("##### The MESSAGE is not Readable ####"); throw ex; }catch(JMSException ex){ log.error("##### JMS provider fails to set the object due to some internal error. ####"); throw ex; } } private void sendTopicMessage(Object objectMessage) throws JMSException, NamingException{ try{ InitialContext initialContext = getInitialContext(); TopicConnectionFactory tcf = (TopicConnectionFactory)initialContext.lookup(CONNECTION_FACTORY_CLUSTERED); TopicConnection topicConn = tcf.createTopicConnection(); Topic topic = (Topic) initialContext.lookup(topicQueueName); TopicSession topicSession = topicConn.createTopicSession(false,TopicSession.AUTO_ACKNOWLEDGE); topicConn.start(); TopicPublisher send = topicSession.createPublisher(topic); ObjectMessage om = topicSession.createObjectMessage(); om.setObject((Serializable)objectMessage); setMessageProperties(om); log.debug("##### Publishing Message to a Topic: " + topicName + "#####"); send.publish(om); send.close(); topicConn.stop(); topicSession.close(); topicConn.close(); }catch(MessageFormatException ex){ log.error("##### The MESSAGE is not Serializable ####"); throw ex; }catch(MessageNotWriteableException ex){ log.error("##### The MESSAGE is not Readable ####"); throw ex; }catch(JMSException ex){ log.error("##### JMS provider fails to set the object due to some internal error. ####"); throw ex; } } private InitialContext getInitialContext() throws NamingException{ Properties jboss = new Properties(); jboss.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); jboss.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces"); jboss.put("java.naming.provider.url", server); return new InitialContext(jboss); } } And the web.xml JMSWeb index.jsp JMSClusteredClient JMSClusteredClient com.blogspot.felipeg48.jms.web.JMSClusteredClient JMSClusteredClient /JMSClusteredClient Happy Clustering!!
May 26, 2010
by Felipe Gutierrez
· 16,766 Views
article thumbnail
Creating a Roulette Wheel Using HTML5 Canvas
HTML5 is really hot all over the web right now so I figured I would drop some HTML5 knowledge on y'all. I have worked with Flash and Flex consistently for the last few years so I can easily drop and manipulate graphics in it, but I haven't done much with HTML5. This lead me to try and challenge myself to recreate something I built in Flash in HTML5. That is what we are going to look at and learn to build in this tutorial. A few things should be mentioned. First IE simply doesn't implement the tag which means it won't work in IE, Google has released Explorer Canvas which fixes this to some extent. I, however, didn't worry about adding that to this tutorial because this post is about creating the content. Also, there are some small things that don't work in other browsers. Finally, Mobile Safari (iPhone, iPod Touch, and iPad) doesn't implement text rendering correctly. To get things rolling we'll first put some simple html down. This includes the tag which is what we're using for this tutorial. The only other element we are going to use is an input button to spin our wheel. I also added a little bit of inline style to place the button. The next thing we are going to do is begin drawing some stuff on to our canvas. This is going to be done in JavaScript in a function I named drawRouletteWheel. The basics of drawing on the in 2D at least involve grabbing a drawing context and then drawing onto it. Sounds complicated doesn't it. Let's look at a little bit of code. var colors = ["#B8D430", "#3AB745", "#029990", "#3501CB", "#2E2C75", "#673A7E", "#CC0071", "#F80120", "#F35B20", "#FB9A00", "#FFCC00", "#FEF200"]; var startAngle = 0; var arc = Math.PI / 6; var ctx; function drawRouletteWheel() { var canvas = document.getElementById("canvas"); if (canvas.getContext) { var outsideRadius = 200; var insideRadius = 125; ctx = canvas.getContext("2d"); ctx.clearRect(0,0,500,500); ctx.strokeStyle = "black"; ctx.lineWidth = 2; for(var i = 0; i < 12; i++) { var angle = startAngle + i * arc; ctx.fillStyle = colors[i]; ctx.beginPath(); ctx.arc(250, 250, outsideRadius, angle, angle + arc, false); ctx.arc(250, 250, insideRadius, angle + arc, angle, true); ctx.stroke(); ctx.fill(); } } } Okay, looking at the start of the function we grab a reference to our canvas object by id - nothing new here. Then we check to make sure the browser supports grabbing a drawing context, this is important to make sure we don't throw errors in browsers that don't support the features. Then we set a couple of variables for the inner and outer radius of our wheel. Now, we get into the meat. The first thing we do is grab a 2D drawing context by calling getContext. From here we clear the canvas, so we have a blank slate to draw onto. Then we set the stroke color and width which in this case is "black" and 2. The next part takes a little bit of explaining, we are going to loop through 12 sections (the number of sides we are going to draw). For each section we determine the angle of where each section is going to start. The startAngle is a global variable in which we are going to use to animate the wheel, for now, it is set to 0. The following line sets the fill color by pulling a value from the global colors array. Drawing begins after that with starting a path drawing two arcs, using the arc(x, y, radius, startAngle, endAngle, anticlockwise) function. We then tell the context to stroke the path and fill the path, these will use the previously set parameters. Now, we are going to add the restaurant text to our drawing code. Also, to finish off the drawing function we'll add the nice little arrow at the top. The text drawing is done using the fillText(text, x, y [, maxWidth ]) function. Let's take a look at the updated code. var restaraunts = ["Wendy's", "McDonalds", "Chick-fil-a", "Five Guys", "Gold Star", "La Mexicana", "Chipotle", "Tazza Mia", "Panera", "Just Crepes", "Arby's", "Indian"]; function drawRouletteWheel() { var canvas = document.getElementById("canvas"); if (canvas.getContext) { var outsideRadius = 200; var textRadius = 160; var insideRadius = 125; ctx = canvas.getContext("2d"); ctx.clearRect(0,0,500,500); ctx.strokeStyle = "black"; ctx.lineWidth = 2; ctx.font = 'bold 12px Helvetica, Arial'; for(var i = 0; i < 12; i++) { var angle = startAngle + i * arc; ctx.fillStyle = colors[i]; ctx.beginPath(); ctx.arc(250, 250, outsideRadius, angle, angle + arc, false); ctx.arc(250, 250, insideRadius, angle + arc, angle, true); ctx.stroke(); ctx.fill(); ctx.save(); ctx.shadowOffsetX = -1; ctx.shadowOffsetY = -1; ctx.shadowBlur = 0; ctx.shadowColor = "rgb(220,220,220)"; ctx.fillStyle = "black"; ctx.translate(250 + Math.cos(angle + arc / 2) * textRadius, 250 + Math.sin(angle + arc / 2) * textRadius); ctx.rotate(angle + arc / 2 + Math.PI / 2); var text = restaraunts[i]; ctx.fillText(text, -ctx.measureText(text).width / 2, 0); ctx.restore(); } //Arrow ctx.fillStyle = "black"; ctx.beginPath(); ctx.moveTo(250 - 4, 250 - (outsideRadius + 5)); ctx.lineTo(250 + 4, 250 - (outsideRadius + 5)); ctx.lineTo(250 + 4, 250 - (outsideRadius - 5)); ctx.lineTo(250 + 9, 250 - (outsideRadius - 5)); ctx.lineTo(250 + 0, 250 - (outsideRadius - 13)); ctx.lineTo(250 - 9, 250 - (outsideRadius - 5)); ctx.lineTo(250 - 4, 250 - (outsideRadius - 5)); ctx.lineTo(250 - 4, 250 - (outsideRadius + 5)); ctx.fill(); } } Looking at the new text drawing we start by saving the current context state - this is going to allow us to rotate and translate the text without affecting everything else. We then set some shadow stuff, which will put a drop shadow on the text. Translating and rotating the text is tackled next, we first translate the text to the correct placement on the wheel and then rotate it. Drawing the text follows this but with the added effect of centering the text by measuring it and dividing that by 2 to offset it. You'll notice we grab the restaurant name from a global array. Lastly we restore to our initial state we saved to, this makes sure the transformations do not affect later drawing. The arrow is even easier to explain, we just move to one corner and draw lines to create the shape and fill it with black. Below I have the complete code for this demo. I'll explain the spinning and animating code right after. var colors = ["#B8D430", "#3AB745", "#029990", "#3501CB", "#2E2C75", "#673A7E", "#CC0071", "#F80120", "#F35B20", "#FB9A00", "#FFCC00", "#FEF200"]; var restaraunts = ["Wendy's", "McDonalds", "Chick-fil-a", "Five Guys", "Gold Star", "La Mexicana", "Chipotle", "Tazza Mia", "Panera", "Just Crepes", "Arby's", "Indian"]; var startAngle = 0; var arc = Math.PI / 6; var spinTimeout = null; var spinArcStart = 10; var spinTime = 0; var spinTimeTotal = 0; var ctx; function drawRouletteWheel() { var canvas = document.getElementById("canvas"); if (canvas.getContext) { var outsideRadius = 200; var textRadius = 160; var insideRadius = 125; ctx = canvas.getContext("2d"); ctx.clearRect(0,0,500,500); ctx.strokeStyle = "black"; ctx.lineWidth = 2; ctx.font = 'bold 12px Helvetica, Arial'; for(var i = 0; i < 12; i++) { var angle = startAngle + i * arc; ctx.fillStyle = colors[i]; ctx.beginPath(); ctx.arc(250, 250, outsideRadius, angle, angle + arc, false); ctx.arc(250, 250, insideRadius, angle + arc, angle, true); ctx.stroke(); ctx.fill(); ctx.save(); ctx.shadowOffsetX = -1; ctx.shadowOffsetY = -1; ctx.shadowBlur = 0; ctx.shadowColor = "rgb(220,220,220)"; ctx.fillStyle = "black"; ctx.translate(250 + Math.cos(angle + arc / 2) * textRadius, 250 + Math.sin(angle + arc / 2) * textRadius); ctx.rotate(angle + arc / 2 + Math.PI / 2); var text = restaraunts[i]; ctx.fillText(text, -ctx.measureText(text).width / 2, 0); ctx.restore(); } //Arrow ctx.fillStyle = "black"; ctx.beginPath(); ctx.moveTo(250 - 4, 250 - (outsideRadius + 5)); ctx.lineTo(250 + 4, 250 - (outsideRadius + 5)); ctx.lineTo(250 + 4, 250 - (outsideRadius - 5)); ctx.lineTo(250 + 9, 250 - (outsideRadius - 5)); ctx.lineTo(250 + 0, 250 - (outsideRadius - 13)); ctx.lineTo(250 - 9, 250 - (outsideRadius - 5)); ctx.lineTo(250 - 4, 250 - (outsideRadius - 5)); ctx.lineTo(250 - 4, 250 - (outsideRadius + 5)); ctx.fill(); } } function spin() { spinAngleStart = Math.random() * 10 + 10; spinTime = 0; spinTimeTotal = Math.random() * 3 + 4 * 1000; rotateWheel(); } function rotateWheel() { spinTime += 30; if(spinTime >= spinTimeTotal) { stopRotateWheel(); return; } var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal); startAngle += (spinAngle * Math.PI / 180); drawRouletteWheel(); spinTimeout = setTimeout('rotateWheel()', 30); } function stopRotateWheel() { clearTimeout(spinTimeout); var degrees = startAngle * 180 / Math.PI + 90; var arcd = arc * 180 / Math.PI; var index = Math.floor((360 - degrees % 360) / arcd); ctx.save(); ctx.font = 'bold 30px Helvetica, Arial'; var text = restaraunts[index] ctx.fillText(text, 250 - ctx.measureText(text).width / 2, 250 + 10); ctx.restore(); } function easeOut(t, b, c, d) { var ts = (t/=d)*t; var tc = ts*t; return b+c*(tc + -3*ts + 3*t); } drawRouletteWheel(); It's a lot to take in but we'll take it slow. At the bottom of the code you'll see we call drawRouletteWheel, this is too draw the initial wheel when the page loads. We have to update the input button we put on the screen to now call our spin function, which handles spinning the wheel, of course. The updated input follows. Taking a look at the spin function we set a couple global variables that decide how fast we are going to spin and how long we are going to spin. These have a little bit of randomness in them to make things more interesting. Lastly, we call rotateWheel. The rotateWheel function updates the amount of time we have been spinning, checks to see if we should stop, updates spinning speed, draws the wheel, and then calls itself in 30 milliseconds. After checking the time we change the spinning angle, startAngle, this is done using an easing function to slow down the spinning. Then we call our drawing function and use setTimeout and keeps a reference to call our rotate again. The last function we need look at is stopping the wheel, stopRotateWheel. This starts with clearing the timeout which will stop the code from rotating the wheel. Then we use some math to figure out what restaurant is at the top. Finally, we draw the large name of the of the selected restaurant in the middle of the wheel. Well, that's pretty much a wrap on this post. I really like how HTML5 and the canvas tag are coming along. Although it's not quite ready for primetime production work yet. I look forward to the next couple years of web development. If you have any questions on the tutorial or just want to bash on my coding skills feel free to drop a comment.
May 26, 2010
by Charlie Key
· 41,229 Views
article thumbnail
Practical PHP Patterns: Lazy Loading
Lazy initialization is a common procedure in programming, especially in an environment like PHP applications where during a specific HTTP request it's practically sure that there are resources which won't be used at all, and that, if eager loaded, would be simply discarded without being referenced again. The Lazy Loading pattern for stateful objects is a transparent solution for loading entity objects on demand, when they are referenced troughout client code. Its typical usage is in the internals of an ORM or in another kind of Data Mapper. Lazy Loading is not a mapper-specific pattern, but in the environment of enterprise patterns this is the best example of its application. Intent In general, the object graph managed by a Data Mapper can be large at will, and loading the entire graph to satisfy a request which usually involves only a small subset of it is a waste. This is the case when using many-to-many bidirectional relationships, as it is the case with the Unix users and groups model. For example, loading an User will bring back also his referenced groups, which in turn will reference their contained users and so on... In general, it is not possible to always break the relationships in one direction to simplify the model. In this case, full loading is not a waste: it's not feasible unless you have gigabytes of ram to allocate per every request. Another use case for lazy loading of domain model entities is in arbitrary computation to perform when there is no knowledge a priori of the extent of the object graph that will be reached by the method calls (or field access). For example, we may want to do a statistical analysis of the userw reachable from a particular starting point through groups relationships, and stop the research when we have reached a predetermined number of users. In this kind of computation, we cannot know for sure how much JOINs we will have to perform as this is an example of a query which falls out of the scope of SQL. Implementation Lazy Loading is generally implemented with a Proxy pattern, where a fake entity is substituted to the real objects at the boundary of the object graph. This fake entities do not load relationships and fields when they are first inserted in the graph. The Proxy pattern implementation used here is the ghost variant, where the object is essentially a proxy for itself, since the field are loaded and stored in it on the first access. The ghost has only its identifier fields loaded by default, and it is inserted in the Identity Map anyway. The PHP implementation of Lazy Loading mechanism in ORMs has to respect some caveats to work. For starters, the Proxy object substituted for the real one has to conform to the same interface of the original class. This is accomplished via subclassing since there is usually no explicit interface (defined with the interface keyword) for domain objects. This subclass may be generated previous to its usage or at runtime, when it comes the time to insert a proxy in the graph. The methods of the entity, such as getters, setters, or logic-related ones are overridden, and their new implementations call a load() method before delegating to the parent method. The load() private method uses a reference to the Entity Manager or to some internal component of the Orm to load the fields of the object itself, and its relationships. Note that this referenced objects can be proxies in turn. Of course, the load() method has a boolean guard that makes it perform real execution of queries only one time, since if the object has already been loaded its behavior returns to be the original class's one. Lazy Loading can be used also for public properties via __get() and __set() overriding, but it is maybe too much transparent as accessing a public property may cause a query to the database. PHP magic methods are often abused. Performance Lazy Loading may cause performance problems when used extensively. The main issues is that it promotes many small queries to load objects one at the time, instead of a big JOIN which can load the whole object graph subset needed, avoiding the overhead of all the unnecessary queries and a chatty communication. Even if the database is high-performance or not relational or whatever, if it resides on another machine communication over the network will experience longer latency and it is best performed in batches. Parts of the graph to eager load via joins (instead of lazy load) can be specified while querying via the language of choice (HQL, JQL, Linq, DQL) or via programmatic interface (Criteria object). This part of the relational information crosses the boundary of the ORM to reach its client code, but it is mandatory to optimize the generated queries to an acceptable level. One solution is to encapsulate it in Repository implementations, that provide a domain-specific interface and hide all the queries in the object-related language of the ORM. Lazy Loading is complex, and it is perfect for outsourcing to a library like an Orm. Be aware of the issues it causes on performance and you will be able to take advantage of it without causing strain on your database. Example The code sample for this ORM pattern is taken from Doctrine 2, where I contributed part of the code related to lazy loading one-to-one and many-to-one relationships. The class presented here is the ProxyFactory, which generates a proxy object for a particular entity and, if needed, the source code for the proxy class. I have inserted some extension to the docblock comments. * @author Giorgio Sironi * @since 2.0 */ class ProxyFactory { /** The EntityManager this factory is bound to. */ private $_em; /** Whether to automatically (re)generate proxy classes. */ private $_autoGenerate; /** The namespace that contains all proxy classes. */ private $_proxyNamespace; /** The directory that contains all proxy classes. */ private $_proxyDir; /** * Initializes a new instance of the ProxyFactory class that is * connected to the given EntityManager. * * @param EntityManager $em The EntityManager the new factory works for. * @param string $proxyDir The directory to use for the proxy classes. It must exist. * @param string $proxyNs The namespace to use for the proxy classes. * @param boolean $autoGenerate Whether to automatically generate proxy classes. */ public function __construct(EntityManager $em, $proxyDir, $proxyNs, $autoGenerate = false) { if ( ! $proxyDir) { throw ProxyException::proxyDirectoryRequired(); } if ( ! $proxyNs) { throw ProxyException::proxyNamespaceRequired(); } $this->_em = $em; $this->_proxyDir = $proxyDir; $this->_autoGenerate = $autoGenerate; $this->_proxyNamespace = $proxyNs; } /** * Gets a reference proxy instance for the entity of the given type and identified by * the given identifier. * Generalle this method will reuse the source code it has already generated, even in * other HTTP requests since the source file are saved in a configurable folder. * This is the only public method the other parts of the ORM will generally use. * * @param string $className * @param mixed $identifier * @return object */ public function getProxy($className, $identifier) { $proxyClassName = str_replace('\\', '', $className) . 'Proxy'; $fqn = $this->_proxyNamespace . '\\' . $proxyClassName; if ($this->_autoGenerate && ! class_exists($fqn, false)) { $fileName = $this->_proxyDir . DIRECTORY_SEPARATOR . $proxyClassName . '.php'; $this->_generateProxyClass($this->_em->getClassMetadata($className), $proxyClassName, $fileName, self::$_proxyClassTemplate); require $fileName; } if ( ! $this->_em->getMetadataFactory()->hasMetadataFor($fqn)) { $this->_em->getMetadataFactory()->setMetadataFor($fqn, $this->_em->getClassMetadata($className)); } $entityPersister = $this->_em->getUnitOfWork()->getEntityPersister($className); return new $fqn($entityPersister, $identifier); } /** * Generates proxy classes for all given classes. * Used for pre-generation from command line, in case PHP on the hosting service * has not the rights to create new files in the proxies folder. * * @param array $classes The classes (ClassMetadata instances) for which to generate proxies. * @param string $toDir The target directory of the proxy classes. If not specified, the * directory configured on the Configuration of the EntityManager used * by this factory is used. */ public function generateProxyClasses(array $classes, $toDir = null) { $proxyDir = $toDir ?: $this->_proxyDir; $proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; foreach ($classes as $class) { $proxyClassName = str_replace('\\', '', $class->name) . 'Proxy'; $proxyFileName = $proxyDir . $proxyClassName . '.php'; $this->_generateProxyClass($class, $proxyClassName, $proxyFileName, self::$_proxyClassTemplate); } } /** * Generates a proxy class file. * Substitutes certain parameters like class name and methods in a template * kept at the end of this file. The class source code is saved in a file in the directory specified. * Testing this method is usually not a problem since the directory is easily configurable. * * @param $class * @param $originalClassName * @param $proxyClassName * @param $file The path of the file to write to. */ private function _generateProxyClass($class, $proxyClassName, $fileName, $file) { $methods = $this->_generateMethods($class); $sleepImpl = $this->_generateSleep($class); $placeholders = array( '', '', '', '', '' ); if(substr($class->name, 0, 1) == "\\") { $className = substr($class->name, 1); } else { $className = $class->name; } $replacements = array( $this->_proxyNamespace, $proxyClassName, $className, $methods, $sleepImpl ); $file = str_replace($placeholders, $replacements, $file); file_put_contents($fileName, $file); } /** * Generates the methods of a proxy class. * All methods are overridden to call _load() before execution. * * @param ClassMetadata $class * @return string The code of the generated methods. */ private function _generateMethods(ClassMetadata $class) { $methods = ''; foreach ($class->reflClass->getMethods() as $method) { /* @var $method ReflectionMethod */ if ($method->isConstructor() || strtolower($method->getName()) == "__sleep") { continue; } if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) { $methods .= PHP_EOL . ' public function '; if ($method->returnsReference()) { $methods .= '&'; } $methods .= $method->getName() . '('; $firstParam = true; $parameterString = $argumentString = ''; foreach ($method->getParameters() as $param) { if ($firstParam) { $firstParam = false; } else { $parameterString .= ', '; $argumentString .= ', '; } // We need to pick the type hint class too if (($paramClass = $param->getClass()) !== null) { $parameterString .= '\\' . $paramClass->getName() . ' '; } else if ($param->isArray()) { $parameterString .= 'array '; } if ($param->isPassedByReference()) { $parameterString .= '&'; } $parameterString .= '$' . $param->getName(); $argumentString .= '$' . $param->getName(); if ($param->isDefaultValueAvailable()) { $parameterString .= ' = ' . var_export($param->getDefaultValue(), true); } } $methods .= $parameterString . ')'; $methods .= PHP_EOL . ' {' . PHP_EOL; $methods .= ' $this->_load();' . PHP_EOL; $methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');'; $methods .= PHP_EOL . ' }' . PHP_EOL; } } return $methods; } /** * Generates the code for the __sleep method for a proxy class. * The __sleep() method is used in case of serialization, which should * not include service objects referenced by proxies like the Entity Manager. * * @param $class * @return string */ private function _generateSleep(ClassMetadata $class) { $sleepImpl = ''; if ($class->reflClass->hasMethod('__sleep')) { $sleepImpl .= 'return parent::__sleep();'; } else { $sleepImpl .= 'return array('; $first = true; foreach ($class->getReflectionProperties() as $name => $prop) { if ($first) { $first = false; } else { $sleepImpl .= ', '; } $sleepImpl .= "'" . $name . "'"; } $sleepImpl .= ');'; } return $sleepImpl; } /** Proxy class code template */ private static $_proxyClassTemplate = '; /** * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. */ class extends \ implements \Doctrine\ORM\Proxy\Proxy { private $_entityPersister; private $_identifier; public $__isInitialized__ = false; public function __construct($entityPersister, $identifier) { $this->_entityPersister = $entityPersister; $this->_identifier = $identifier; } private function _load() { if (!$this->__isInitialized__ && $this->_entityPersister) { $this->__isInitialized__ = true; if ($this->_entityPersister->load($this->_identifier, $this) === null) { throw new \Doctrine\ORM\EntityNotFoundException(); } unset($this->_entityPersister); unset($this->_identifier); } } public function __sleep() { if (!$this->__isInitialized__) { throw new \RuntimeException("Not fully loaded proxy can not be serialized."); } } }'; }
May 26, 2010
by Giorgio Sironi
· 27,609 Views
article thumbnail
Interview: Music Composer on the NetBeans Platform
Steven Yi (pictured right) is a programmer and composer living in Rochester, NY. He studied music composition in college and became a programmer afterwards. He started off as a Flash and server side developer (which he did for about 7 years), and has spent the past few years at his current company doing mobile development with J2ME, Android, and iPhone, as well as server-side development with Spring, Hibernate, etc. He started learning and using Java and Swing for personal work in 2000 and has been using it since then for the development of blue, the focus of the interview that follows below. In the interview, Steven talks about the "blue" music composer, how it works, and how the NetBeans Platform and Python form the basis of this cool open-sourced Java music composer. What's "blue"? blue is a music composition environment I started in the fall of 2000. It was actually my very first Java program! At the time, I had started using the music software Csound (http://www.csounds.com) to compose, but found it slow to work with when it came to accomplishing what I was interested in musically. I had the idea to create a simple program that would have a timeline and the ability to scale musical score material in time. Fast forward many years later: in trying to solve other musical problems, and responding to feedback from the community of users, I've expanded blue's features a great deal. It now includes things like a mixer and effects system, a GUI builder tool for creating synthesizer interfaces, embedded Jython processing of musical scripts, and more. It's been quite satisfying to create a tool that can express my musical interests and to find a community of users who have found value in this program for their own musical work. Some screenshots: The Orchestra manager shows a BlueSynthBuilder instrument being edited. The "Reson 6" instrument is shown in edit mode. The BSB Object Properties panel shows the properties for the selected knob: The Score timeline shows a project using multiple parameter automations. The values automated include things like volume, panning, and a time pointer for a phase-vocoder instrument. All BlueSynthBuilder instruments, Effects, and the Mixer volume sliders can be automated: The Score timeline showing the author's composition "Reminiscences". The timeline shows multiple Python SoundObjects used. The SoundObject Editor shows the editor for the selected SoundObject in the timeline. The SoundObject Properties panel shows different properties for the selected SoundObject: The Score timeline showing a Tracker SoundObject being used. The timeline is configured to snap at every 4 beats and the time bar has been configured to show in numbers rather than time: The Score timeline showing a PianoRoll SoundObject being used. The PianoRoll is unique in that it is microtonal, meaning it can adapt the number of steps per "octave", depending on the values configured from a tuning file. In this screenshot, the scale loaded was a Bohlen-Pierce scale, which has 13-tones per tritave (octave and a half): The blue Mixer is shown docked into the bottom bar and in an open state. The interfaces for user-created Chorus and Reverb effects are shown. The interfaces were created using the same GUI builder tool that is found in the BlueSynthBuilder instrument: It's got a very special appearance. How did that come about? blue's custom look and feel started off one day when I was using my Palm PDA. I remember thinking that I enjoyed the look of the device with the backlight on, and so I wanted to recreate that kind of look for my program. Later, I modified the color scheme to tone it down in some ways, but I also introduced more colors than white and cyan to highlight secondary and tertiary features. Maybe now it is now more like Tron than it is like Palm. :) Overall, I enjoy the darker look of the application when I'm working on music. I tend to work on music when I have free time, and that is usually only late at night—I've found having a darker screen has been easier on my eyes. Also, if anyone was wondering, yes, blue is my favorite color. The blue look and feel is encapsulated in a module named "blue-plaf" and is available in the "blue" Mercurial repository (http://bluemusic.hg.sourceforge.net/hgweb/bluemusic/blue). The look and feel is quite hacked up (redoing it properly has been another item on my todo list), but it can be dropped into another application and it should work, as shown below with the CRUD Sample (which can be created from a tutorial found here): Can you explain how blue's timeline works? blue has a concept of SoundLayers and SoundObjects. SoundObjects are objects that primarily produce notes and have a start and duration. There are many different types of SoundObjects in blue and each has an editor (viewed in the SoundObject Editor TopComponent when a SoundObject is selected), and a BarRenderer, which is used to draw the content area of the bar on the timeline. A PolyObject is a special SoundObject. It consists of SoundLayers, which contain SoundObjects. The root timeline is itself just a PolyObject that you can add as many layers to as you like. You can also group individual SoundObjects into their own PolyObjects, and then use the resulting PolyObjects just like any other SoundObject on the timeline. If you double-click a PolyObject, the timeline is then reset with the timeline of the PolyObject you selected. As a result, PolyObjects allow timelines to be embedded within other timelines. If you think about how music is grouped into motives, phrases, sections, and even larger groupings, you can see how PolyObjects might represent these kinds of musical abstractions. For the component design, the ScoreTopComponent starts off with a JSplitPane to split between a SoundLayerListPanel on the left and a JScrollPane on the right. The JScrollPane has a ScoreTimeCanvas (the main timeline) in the main viewPort's view, a panel with the the time bar and tempo editor in the column header, and the corner is used to open up an extra panel to modify properties for the timeline. The JScrollPane has customized JScrollBars used to add the ± buttons that perform zooming on the timeline. There are a number of other features involved that are implemented amongst a number of classes, but the details of how viewPorts are synchronized (among other things) may be a bit too technical to discuss here. For those who are interested, the code can be viewed within the blue.ui.core.score package within the blue-ui-core module. How did blue come to find itself atop the NetBeans Platform? I first started to be interested in NetBeans IDE around the time 4.1 came out, but didn't really get into using it until the release of 5.0. At that time, I had hand-written Swing components for about 4-5 years (I don't really remember when 5.0 was released), and I found Matisse to be quite nice and began using it here and there. I had looked at the NetBeans Platform as an RCP at that time, but found it to be quite a bit to understand. However, I still kept it on my radar. Around the time 6.0 or 6.5 came out, I started to reconsider migrating to the NetBeans Platform once again. By this time, I had moved over to using NetBeans IDE full time for blue development and had been using NetBeans IDE more in general—particularly Java Web development and Ruby on Rails. One of the biggest things I found attractive about NetBeans IDE is its windowing system... and the things I read about in the platform development articles I'd seen online made me curious once again to see what the NetBeans Platform offered. I still felt that there was going to be a big learning curve to learn the NetBeans Platform, but the NetBeans Platform tutorials online were really quite helpful, as were the members of the NetBeans Platform mailing list, and there were also many more books available to help me get started. I think I ultimately spent about 6-8 months migrating blue to using the NetBeans Platform. Granted, it was a busy time in my life and I was working on this only in my spare time, so I think in the end it was a reasonable amount of time. Users have been very positive about the new blue interface and application as a whole, and I think it has been worth spending the time to use the NetBeans Platform. blue's window layout is quite unexpected for a NetBeans Platform application. By the time I had started migrating blue to the NetBeans Platform, the application was already some 7-8 years old. The interface I designed for blue in pure Swing was influenced by my experiences in using Flash, looking at other music composition environments (Digital Audio Workstations and Sequencing Programs), and evaluating the different aspects of working with Csound. Mapping the components from the Swing-based application to the NetBeans Platform was a little tricky in that I couldn't quite get the exact same design of panels as I had in pure Swing. In the end, I tried to think about where most of the components resided physically, and created TopComponents and placed them in the center, left, right, or bottom parts of the main window. I kept some of the dialogs from the old codebase as-is, but I migrated others to be TopComponents so that they could be docked, opened, or dragged out into a dialog as the user wished. In the end, the GUI is different and took a little getting used to after years of using and building the old interface, but I quickly adjusted to the changes and I think there is much greater consistency and usability now. The users have responded very positively to the general polish of the application and to being able to customize their environment. I myself have very much enjoyed being able to dock all of the windows as well as using full-screen mode, especially when I am on my netbook and composing. Excellent! What features of the NetBeans Platform are you using and what do you find to be most useful? Currently, I am using only a very small part of the NetBeans Platform. By the time I started to move my code to the NetBeans Platform, the codebase was already some 7 or 8 years old. I took the approach recommended to me on the mailing list and started off small, focusing primarily on migrating my project to using the Windows System API, the Options API, and a few other utility API's like IO and Dialogs. Having an old codebase, I found that I spent most of my time during migration just reorganizing my UI into TopComponents and working out communications between the components. I also spent time looking at API's that I had developed myself and seeing which ones could be replaced with API's provided by the NetBeans Platform. At this time, the application is still using a number of API's I wrote from the old codebase, but over time I would like to migrate more of the appplication to use the Nodes and Visual Library API's. I think migrating a codebase of this size in phases really worked out well. In the first phase, I was able to take advantage of the Window System API and have a very visible result on the application and gained a lot for usability. Also, a big part of the migration involved moving the codebase from a monolithic source tree and partitioning it into logical modules. I think there really is a great deal of benefit to working with a codebase with modular design, and that too is a very positive result of working with the NetBeans Platform. Please say something about how Jython relates to this application, how you are using it (what the benefits are), and your general opinions on Jython. I have had a Python SoundObject in blue for quite some time—I think since 2002. For me, it is one of the most important tools in blue when it comes to accomplishing what I want musically. With computer music, we have a lot of tools for what I call Common Practice computer music: PianoRolls, Pattern Editors, and Notation Objects. For computer musicians who are interested in Uncommon Practice music, the ability to use a scripting language opens up a number of ways to express musical ideas that cannot be easily conveyed using those other tools. In blue, Jython is primarily used to allow users to write scripts that will generate notes. For myself, I use Python scripts to model orchestral composition, creating Performer and PerformerGroup objects that I write in Python. I also write performance functions, usually per-project, to perform different musical material in different ways. Other users have used Python scripts in exploring things like algorithmic composition and genetic algorithms in their work. A blue project can contain any number of Python objects. The score generated by each Python object is translated and scaled in time by moving and resizing the SoundObject in the timeline. This allows a user who may want to use scripting to create musical material to also take advantage of blue's timeline to organize how the different musical objects will work together. One of the things I most appreciate about Jython (and scripting languages on the JVM in general) is that it is embeddable within a Java application. By packaging and embedding the Jython interpreter within the blue application, users can rest assured that the Python scripts they write can be interpreted anywhere that blue is installed. It's an extra assurance that their musical projects will be long-lasting, but they can still take advantage of a full programming language like Python in their work. Overall, I think that Jython is a fine piece of software and I hope that it will continue to grow and develop for years to come. Is the application open source and are you looking for code contributions and, if so, in which areas? Yes, the application is available under the GPL v2 license, and the source code can be viewed from the Mercurial repository on SourceForge at http://bluemusic.hg.sourceforge.net/hgweb/bluemusic/blue/. I am a strong proponent of open source, especially for creative work. In the same way that we can today look at and study musical works by composers of the past (like Josquin and Bach), I would like to imagine that the work composers and other artists are now creating with computers will also be open and available for study in the future. I believe that using open source software for creative work greatly helps in making musical projects available for the years ahead. I have done most of the development of blue myself, and over the years I've certainly built up a long list of things that I would like to implement. Users have also made wonderful feature requests that I would love to see in the program—but unfortunately, there are only so many hours in the day. It would certainly be nice to have others contributing code! Beyond new features, there are a number of infrastructural things that would be nice to address. The codebase is many years old, and while the application has been refactored multiple times over its lifetime, there are still some areas of the application that could be much more cleanly implemented. Also, in moving over to the NetBeans Platforms, I only really took the first steps. There are a number of components within the application that could probably be better served by migrating to using more of the NetBeans API's. For internal work, things like modifying the timeline to implement zooming to use Graphics2d and transforms, implementing a better waveform renderer for audio files, and further enhancing the instrument GUI builder are all things I'd like to see. I'd also love to get help in migrating all of the tables and trees to using the Nodes API, something that I have not yet had the time to do. It would also be nice to get the manual (currently in HTML and PDF, generated from DocBook) integrated into the application as JavaHelp, but this is another thing that I have had to postpone due to lack of time. For features, some interesting things I'd love to see are a Notation SoundObject, a separate graphical instrument builder using the Visual Library API, and a Sampler instrument. There's also a sound drawing SoundObject, enhancements to existing SoundObjects, and more I'd love to see moving forward. Maybe someone will find these kinds of things interesting and will take a look at blue's code sometime! Thanks Steven and happy music making with blue!
May 25, 2010
by Geertjan Wielenga
· 17,917 Views
article thumbnail
Writing Cucumber Step Definitions in JavaScript
Cucumber is a Behavior-Driven Development tool that lets developers describe their software's behavior in plain text using a business-readable DSL (Domain-Specific Language). Project developers have added a useful adapter for Cucumber which allows users to write step definitions in JavaScript instead of Ruby (described in Joseph Wilk's blog). To use Cucumber, you previously needed to know a slight amount of Ruby, now you can completely forgo using Ruby if you know a little JavaScript. Cucumber supports testing for Java, Ruby, .Net, Flex, Python, web languages, and more. Here are the home page's seven steps for using Cucumber: Describe behaviour in plain text Write a step definition in Ruby (Now you can do this in pure JS!) Run and watch it fail Write code to make the step pass Run again and see the step pass Repeat 2-5 until green like a cuke Repeat 1-6 until the money runs out The new adapter in Cucumber is able to provide JS support for step definitions through TheRubyRacer. This tool allowed Cucumber developers to build the JS adapter by embedding Google's V8 JavaScript interpreter into Ruby. Here is an example of the feature: Feature: Fibonacci In order to calculate super fast fibonacci series As a Javascriptist I want to use Javascript for that @fibonacci Scenario Outline: Series When I ask Javascript to calculate fibonacci up to Then it should give me Examples: | n | series | | 1 | [] | | 2 | [1, 1] | | 3 | [1, 1, 2] | | 4 | [1, 1, 2, 3] | | 6 | [1, 1, 2, 3, 5] | | 9 | [1, 1, 2, 3, 5, 8] | | 100 | [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] | And the step definitions in JS: Before(['@fibonacci'], function(){ fibResult = 0; }); When(/^I ask Javascript to calculate fibonacci up to (\d+)$/, function(n){ assertEqual(0, fibResult) fibResult = fibonacciSeries(n); }); Then(/^it should give me (\[.*\])$/, function(expectedResult){ assertEqual(expectedResult, fibResult) }); Cucumber developers have tried to make the JS API and the Ruby API as similar as possible, but the JS API currently doesn't have support for calling step definitions within step definitions with multi-line arguments. It also doesn't support line reporting on step definitions. The JS API also has a different way for loading code into the 'World' to make sure it is in scope within the step definitions. For this kind of folder structure: my_js_project/lib/code_lives_here.js my_js_project/features/support/env.js my_js_project/features/my_feature.feature There would be this code within the features/support/env.js setup file: //Cucumber resolves the files relative to the folder that contains the features folder. World(['lib/code_lives_here.js']) Code inside the code_lives_here.js file would be available in the step definitions.
May 24, 2010
by Mitch Pronschinske
· 24,430 Views
article thumbnail
Practical PHP Patterns: Identity Map
The Identity Map pattern is a Map implementation related to a Data Mapper usage. A map in the computer science sense is also called dictionary, or associative array; although in PHP associative arrays are very powerful, this kind of Map can be implemented as an object to present a specific interface to client code. The purpose of an Identity Map in the Data Mapper context is to keep a list of all the references to the in-memory domain objects that has been reconstituted by the Data Mapper internal mechanism, or are somehow managed by the Data Mapper itself (for example they have been scheduled for persistence). The Identity Map solves the problem of multiple loading of objects, which leads to performance issues and inconsistencies like two different objects with different states (but whose identity is the same, since they have for example an equal user id) that has to be stored in the back end. Ideally has a reference to every single object of the domain (that contains state, and thus is managed by the Data Mapper instead of being created by infrastructure or domain factories), in practice it is an array of references to the loaded objects. PHP implementation In PHP, the Identity Map is not unique troughout the whole application, but it is an object whose scope is limited to the single HTTP request (and so for example different requests have different Identity Maps which can become inconsistent with each other.) This limited scope, which is part of the nature of PHP and its scalable architecture, requires careful handling of objects that have been detached from the Data Mapper. In general, you can serialize or store in a cache domain objects for performance boost or simplification of business logic. You have, however, the obligation of reattaching a domain object to the Data Mapper with a special method (in Doctrine 2 EntityManager::merge()) to subsequently persist it, so that it can be reinserted in the Identity Map instead of being considered new or being duplicated. Remember that here duplication is more an issue of consistency than performance: a Data Mapper which accepts two different objects that points to the same place in the data store is not reliable. In fact, an Identity Map is a fundamental part of a non-naive Data Mapper: before recreating an entity the mapper looks for it in the Identity Map, to check if it is already available. Only if the object is not there, the Data Mapper creates a new one and inserts it in the Map for later reuse. Thus the Identity Map bridges the gap between the storage and the memory, keeping track of which parts of the object graph have been brought in memory and which are still on disks or external database machines, since we are forced by the technology to actually reconstitute a very small part of the application state in the form of objects (to be able to work on them). This approach is particularly suited to PHP's shared nothing mentality: there are other solutions for languages like Java and C#, like keeping the whole object graph in memory (some gigabytes) and dealing with persistence by taking a periodical snapshot of the graph, which is then freezed and stored on slower-but-larger memories like disks or SSD. In Doctrine 2 From the technical point of view, the Identity Map is an object or an associative array, with a single instance that exists for the entire request. This data structure is composed by the Entity Manager (the Facade of the Data Mapper) or by a Unit of Work, or even by some internal class of the Data Mapper. Even when an object is reconstituted as part of a query and not requested by its primary key, the loader class has to extract a unique identifier for the domain object and ask the Identity Map. In the sample code we will see at the end of this article, Doctrine 2 choice has been to keep the Identity Map as a private property (a multidimensional associative array) of the Unit of Work, which has a set of public methods available to access the Map to act as the unique Facade for the internal code. The tipical key used for the indexing is a combination of the class name of the domain object and of its unique identifier (usually the primary key used in storage, reduced to a serialized value if constituted by multiple fields.) This indexing implementation is generic enough to deal with most of the use cases, even with inheritance strategies. Another supplemental indexing is based on the spl_object_hash() function result, which returns a unique identifier for every in-memory object; this indexing is used to quickly check if an object originated from somewhere is in the Identity Map, without extracting its identifier and class name. The sample code is part of the Unit of Work of Doctrine 2. I cut all the aspects which did not involve its internal Identity Map as we have already described it in its own article. * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel * @internal This class contains highly performance-sensitive code. */ class UnitOfWork implements PropertyChangedListener { //... /** * The identity map that holds references to all managed entities that have * an identity. The entities are grouped by their class name. * Since all classes in a hierarchy must share the same identifier set, * we always take the root class name of the hierarchy. * * @var array */ private $_identityMap = array(); /** * Map of all identifiers of managed entities. * Keys are object ids (spl_object_hash). * * @var array */ private $_entityIdentifiers = array(); /** * INTERNAL: * Registers an entity in the identity map. * Note that entities in a hierarchy are registered with the class name of * the root entity. * * @ignore * @param object $entity The entity to register. * @return boolean TRUE if the registration was successful, FALSE if the identity of * the entity in question is already managed. */ public function addToIdentityMap($entity) { $classMetadata = $this->_em->getClassMetadata(get_class($entity)); $idHash = implode(' ', $this->_entityIdentifiers[spl_object_hash($entity)]); if ($idHash === '') { throw new \InvalidArgumentException("The given entity has no identity."); } $className = $classMetadata->rootEntityName; if (isset($this->_identityMap[$className][$idHash])) { return false; } $this->_identityMap[$className][$idHash] = $entity; if ($entity instanceof NotifyPropertyChanged) { $entity->addPropertyChangedListener($this); } return true; } /** * INTERNAL: * Removes an entity from the identity map. This effectively detaches the * entity from the persistence management of Doctrine. * * @ignore * @param object $entity * @return boolean */ public function removeFromIdentityMap($entity) { $oid = spl_object_hash($entity); $classMetadata = $this->_em->getClassMetadata(get_class($entity)); $idHash = implode(' ', $this->_entityIdentifiers[$oid]); if ($idHash === '') { throw new \InvalidArgumentException("The given entity has no identity."); } $className = $classMetadata->rootEntityName; if (isset($this->_identityMap[$className][$idHash])) { unset($this->_identityMap[$className][$idHash]); $this->_entityStates[$oid] = self::STATE_DETACHED; return true; } return false; } /** * Checks whether an entity is registered in the identity map of this UnitOfWork. * * @param object $entity * @return boolean */ public function isInIdentityMap($entity) { $oid = spl_object_hash($entity); if ( ! isset($this->_entityIdentifiers[$oid])) { return false; } $classMetadata = $this->_em->getClassMetadata(get_class($entity)); $idHash = implode(' ', $this->_entityIdentifiers[$oid]); if ($idHash === '') { return false; } return isset($this->_identityMap[$classMetadata->rootEntityName][$idHash]); } }
May 24, 2010
by Giorgio Sironi
· 8,789 Views
article thumbnail
Practical PHP Patterns: Unit of Work
The Unit of Work pattern is one of the most complex moving parts of Object-Relational Mappers, and usually of Data Mappers in general. A Unit of Work is a component (for us, an object with collaborators) which keeps track of the new, modified and deleted domain objects whose changes have to be reflected in the data store. At at the end of a transaction the Unit of Work, if used correctly, is capable of producing a list of changes to perform on the data store, solving concurrency or consistency problems, and avoiding too many redundant queries in the relational case or a chatty communication in the schemaless one. As I've already said, the Unit of Work pattern is usually not employed alone but as part of a Data Mapper, which provides a different interface to the internal client code and mixes up this pattern with several other ones. The minimum transaction that a PHP Unit of Work performs is usually an HTTP request, or a session composed by more than one request in case the domain objects can be saved in an intermediate store (like $_SESSION or a cache of any kind). Being able to serialize objects in a store and reattaching them to the Unit of Work during subsequent requests is not a trivial problem. Advantages The power of a Unit of Work resides in the fact that the actual database transaction is only performed (and kept opened) when the commit() method of the Unit of Work is called, while until that moment there is ideally no use of the database connection. This paradigm is called batch update. Objects stored in a Unit of Work have usually an associated state, like: new (which correspondes to INSERT queries during the batch update) clean (no SQL queries have to be issued since the object has been retrieved and not modified) dirty (UPDATE queries) removed (DELETE queries) There are different strategies for detecting changes to the object graph. The simplest strategy is comparing objects with a clean copy kept in memory (while it is usually not performance-wise to compare them with the database.) A more complex solution is having a specific interface which is implemented by the objects, so that they can manage their state and declare they are dirty or have to be removed. This implementation choice introduces a dependency from the domain layer to the infrastructure one, thus I prefer heavier approaches like the former, which is equivalent to generate a diff with your source control system of choice, but on the object graph instead of a codebase: the source files are not responsible for diffing themselves. Furthermore, the Unit of Work decoupling from the database state introduces an upper level of management, that makes us able to rollback changes if some constraint are not satisfied, or the computation has produced an error. In PHP, the client code can simply throw the object graph away, and the partial Unit of Work changeset is forgotten in the next requests. Issues While decoupling the object graph from the data store to perform custom computations is a comfortable possibility for the client code, at the same time it can be an issue that introduces stale data. The more the objects are kept in the Unit of Work, the more the data store is prone to external concurrent modifications inconsistent with the in-memory graph (for example updating fields with different values than the ones modified in this very session.) Either a optimistic or pessimistic locking mechanism has to be introduced when the scope of the object graph is longer than the few seconds necessary of producing an HTTP response, or even less than that when the traffic is higher. Injecting the Unit of Work in the domain objects so that they can track their state can be problematic and too much an invasion of the domain layer. Usually the problem is solved the other way around: when the objects are passed to the Object-Relational Mapper (almost always implemented as a Data Mapper and not as an Active Record), it delegates part of the logic to the Unit of Work, which is a first-class citizen and can be tested independently from the other components of the library. The alternative to the inherent complexity of the Unit of Work pattern is saving an object at the moment it is updated. This solution is problematic because either the client code has to explicitly call save() methods, or queries (read modification to the data store in case of non-relational model) have to be performed at the very time of an atomic change, for instance issuing multiple UPDATE statements, one for every time a field is modified. Example The sample code of this article is the internal API of Doctrine 2. The actual Unit of Work code is dependent on the strategy adopted to detect changes to domain objects, but the interface exposed to the Entity Manager is always the same and should provide a panoramic of an Unit of Work's responsibilities and features. In this implementation, the methods persist() and remove() are used to introduce new objects to the Unit of Work or to schedule something for deletion from the database, while commit() executes a batch update on demand. * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel * @internal This class contains highly performance-sensitive code. */ class UnitOfWork implements PropertyChangedListener { /** * An entity is in MANAGED state when its persistence is managed by an EntityManager. */ const STATE_MANAGED = 1; /** * An entity is new if it has just been instantiated (i.e. using the "new" operator) * and is not (yet) managed by an EntityManager. */ const STATE_NEW = 2; /** * A detached entity is an instance with a persistent identity that is not * (or no longer) associated with an EntityManager (and a UnitOfWork). */ const STATE_DETACHED = 3; /** * A removed entity instance is an instance with a persistent identity, * associated with an EntityManager, whose persistent state has been * deleted (or is scheduled for deletion). */ const STATE_REMOVED = 4; /** * Commits the UnitOfWork, executing all operations that have been postponed * up to this point. The state of all managed entities will be synchronized with * the database. * * The operations are executed in the following order: * * 1) All entity insertions * 2) All entity updates * 3) All collection deletions * 4) All collection updates * 5) All entity deletions * */ public function commit() { // Compute changes done since last commit. $this->computeChangeSets(); if ( ! ($this->_entityInsertions || $this->_entityDeletions || $this->_entityUpdates || $this->_collectionUpdates || $this->_collectionDeletions || $this->_orphanRemovals)) { return; // Nothing to do. } if ($this->_orphanRemovals) { foreach ($this->_orphanRemovals as $orphan) { $this->remove($orphan); } } // Raise onFlush if ($this->_evm->hasListeners(Events::onFlush)) { $this->_evm->dispatchEvent(Events::onFlush, new Event\OnFlushEventArgs($this->_em)); } // Now we need a commit order to maintain referential integrity $commitOrder = $this->_getCommitOrder(); $conn = $this->_em->getConnection(); $conn->beginTransaction(); try { if ($this->_entityInsertions) { foreach ($commitOrder as $class) { $this->_executeInserts($class); } } if ($this->_entityUpdates) { foreach ($commitOrder as $class) { $this->_executeUpdates($class); } } // Extra updates that were requested by persisters. if ($this->_extraUpdates) { $this->_executeExtraUpdates(); } // Collection deletions (deletions of complete collections) foreach ($this->_collectionDeletions as $collectionToDelete) { $this->getCollectionPersister($collectionToDelete->getMapping()) ->delete($collectionToDelete); } // Collection updates (deleteRows, updateRows, insertRows) foreach ($this->_collectionUpdates as $collectionToUpdate) { $this->getCollectionPersister($collectionToUpdate->getMapping()) ->update($collectionToUpdate); } // Entity deletions come last and need to be in reverse commit order if ($this->_entityDeletions) { for ($count = count($commitOrder), $i = $count - 1; $i >= 0; --$i) { $this->_executeDeletions($commitOrder[$i]); } } $conn->commit(); } catch (Exception $e) { $this->_em->close(); $conn->rollback(); throw $e; } // Take new snapshots from visited collections foreach ($this->_visitedCollections as $coll) { $coll->takeSnapshot(); } // Clear up $this->_entityInsertions = $this->_entityUpdates = $this->_entityDeletions = $this->_extraUpdates = $this->_entityChangeSets = $this->_collectionUpdates = $this->_collectionDeletions = $this->_visitedCollections = $this->_scheduledForDirtyCheck = $this->_orphanRemovals = array(); } /** * Computes the changes that happened to a single entity. * * Modifies/populates the following properties: * * {@link _originalEntityData} * If the entity is NEW or MANAGED but not yet fully persisted (only has an id) * then it was not fetched from the database and therefore we have no original * entity data yet. All of the current entity data is stored as the original entity data. * * {@link _entityChangeSets} * The changes detected on all properties of the entity are stored there. * A change is a tuple array where the first entry is the old value and the second * entry is the new value of the property. Changesets are used by persisters * to INSERT/UPDATE the persistent entity state. * * {@link _entityUpdates} * If the entity is already fully MANAGED (has been fetched from the database before) * and any changes to its properties are detected, then a reference to the entity is stored * there to mark it for an update. * * {@link _collectionDeletions} * If a PersistentCollection has been de-referenced in a fully MANAGED entity, * then this collection is marked for deletion. * * @param ClassMetadata $class The class descriptor of the entity. * @param object $entity The entity for which to compute the changes. */ public function computeChangeSet(Mapping\ClassMetadata $class, $entity) { // ... } /** * Computes all the changes that have been done to entities and collections * since the last commit and stores these changes in the _entityChangeSet map * temporarily for access by the persisters, until the UoW commit is finished. */ public function computeChangeSets() { // ... } /** * Schedules an entity for insertion into the database. * If the entity already has an identifier, it will be added to the identity map. * * @param object $entity The entity to schedule for insertion. */ public function scheduleForInsert($entity) { $oid = spl_object_hash($entity); if (isset($this->_entityUpdates[$oid])) { throw new \InvalidArgumentException("Dirty entity can not be scheduled for insertion."); } if (isset($this->_entityDeletions[$oid])) { throw new \InvalidArgumentException("Removed entity can not be scheduled for insertion."); } if (isset($this->_entityInsertions[$oid])) { throw new \InvalidArgumentException("Entity can not be scheduled for insertion twice."); } $this->_entityInsertions[$oid] = $entity; if (isset($this->_entityIdentifiers[$oid])) { $this->addToIdentityMap($entity); } } /** * Schedules an entity for being updated. * * @param object $entity The entity to schedule for being updated. */ public function scheduleForUpdate($entity) { $oid = spl_object_hash($entity); if ( ! isset($this->_entityIdentifiers[$oid])) { throw new \InvalidArgumentException("Entity has no identity."); } if (isset($this->_entityDeletions[$oid])) { throw new \InvalidArgumentException("Entity is removed."); } if ( ! isset($this->_entityUpdates[$oid]) && ! isset($this->_entityInsertions[$oid])) { $this->_entityUpdates[$oid] = $entity; } } /** * INTERNAL: * Schedules an entity for deletion. * * @param object $entity */ public function scheduleForDelete($entity) { $oid = spl_object_hash($entity); if (isset($this->_entityInsertions[$oid])) { if ($this->isInIdentityMap($entity)) { $this->removeFromIdentityMap($entity); } unset($this->_entityInsertions[$oid]); return; // entity has not been persisted yet, so nothing more to do. } if ( ! $this->isInIdentityMap($entity)) { return; // ignore } $this->removeFromIdentityMap($entity); if (isset($this->_entityUpdates[$oid])) { unset($this->_entityUpdates[$oid]); } if ( ! isset($this->_entityDeletions[$oid])) { $this->_entityDeletions[$oid] = $entity; } } /** * Checks whether an entity is scheduled for insertion, update or deletion. * * @param $entity * @return boolean */ public function isEntityScheduled($entity) { $oid = spl_object_hash($entity); return isset($this->_entityInsertions[$oid]) || isset($this->_entityUpdates[$oid]) || isset($this->_entityDeletions[$oid]); } public function persist($entity) { $visited = array(); $this->_doPersist($entity, $visited); } /** * Saves an entity as part of the current unit of work. * This method is internally called during save() cascades as it tracks * the already visited entities to prevent infinite recursions. * * NOTE: This method always considers entities that are not yet known to * this UnitOfWork as NEW. * * @param object $entity The entity to persist. * @param array $visited The already visited entities. */ private function _doPersist($entity, array &$visited) { $oid = spl_object_hash($entity); if (isset($visited[$oid])) { return; // Prevent infinite recursion } $visited[$oid] = $entity; // Mark visited $class = $this->_em->getClassMetadata(get_class($entity)); $entityState = $this->getEntityState($entity, self::STATE_NEW); switch ($entityState) { case self::STATE_MANAGED: // Nothing to do, except if policy is "deferred explicit" if ($class->isChangeTrackingDeferredExplicit()) { $this->scheduleForDirtyCheck($entity); } break; case self::STATE_NEW: if (isset($class->lifecycleCallbacks[Events::prePersist])) { $class->invokeLifecycleCallbacks(Events::prePersist, $entity); } if ($this->_evm->hasListeners(Events::prePersist)) { $this->_evm->dispatchEvent(Events::prePersist, new LifecycleEventArgs($entity, $this->_em)); } $idGen = $class->idGenerator; if ( ! $idGen->isPostInsertGenerator()) { $idValue = $idGen->generate($this->_em, $entity); if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) { $this->_entityIdentifiers[$oid] = array($class->identifier[0] => $idValue); $class->setIdentifierValues($entity, $idValue); } else { $this->_entityIdentifiers[$oid] = $idValue; } } $this->_entityStates[$oid] = self::STATE_MANAGED; $this->scheduleForInsert($entity); break; case self::STATE_DETACHED: throw new \InvalidArgumentException( "Behavior of persist() for a detached entity is not yet defined."); case self::STATE_REMOVED: // Entity becomes managed again if ($this->isScheduledForDelete($entity)) { unset($this->_entityDeletions[$oid]); } else { //FIXME: There's more to think of here... $this->scheduleForInsert($entity); } break; default: throw ORMException::invalidEntityState($entityState); } $this->_cascadePersist($entity, $visited); } /** * Deletes an entity as part of the current unit of work. * * @param object $entity The entity to remove. */ public function remove($entity) { $visited = array(); $this->_doRemove($entity, $visited); } /** * Deletes an entity as part of the current unit of work. * * This method is internally called during delete() cascades as it tracks * the already visited entities to prevent infinite recursions. * * @param object $entity The entity to delete. * @param array $visited The map of the already visited entities. * @throws InvalidArgumentException If the instance is a detached entity. */ private function _doRemove($entity, array &$visited) { // ... } }
May 19, 2010
by Giorgio Sironi
· 13,808 Views
article thumbnail
Practical PHP Patterns: Data Mapper
Data Mapper is one of the most advanced persistence-related patterns: an implementation of a Data Mapper stores objects (in general a whole object graph) in a database, and decouples the object model from the backend data representation, moving objects back and forth from the data store without introducing hardcode dependencies towards it. The database back end used by most of the implementations are usually relational: Object-relational mappers are some of the widely used tools today (and are even fading in some areas where different types of database are preferred.) Dependencies The interfaces for a Data Mapper can be put in the domain layer, but actual implementatons are in the category of infrastructure adapters and should be kept out of it, to promote the reuse and testing of domain layer classes without the need for a database back end or driver to be present. When this pattern is employed in an application, there are no more dependencies from the domain layer to external components, and no subclassing like in the Active Record case. Domain entities and value objects become Plain Old PHP Objects which do not extend anything (extends keyword) and do not need to reflect any database schema (if they are saved in a relational db), ensuring the maximum freedom of modelling to the developers. Different kind of implementations Early implementations of Data Mapper did not store an inner reference to a database connection or object that represent the link with the data store; in this case, result sets or some kind of raw data are passed to the Data Mapper, which reconstitutes the objects and encapsulates the process. Currently it is preferred to put all the references to the database as internals of the Data Mapper implementation (or in an abstraction layer under it). Anyway the Data Mapper hides as much as possible, like the type of the database and related knowledge, from the client code (domain layer or an upper one). The interface of the modern Data Mappers become from store() (insert and update) and remove() to one that comprehends also find() methods or a more complex system of querying; the implementation of querying is out of the scope of this pattern, but can be mixed up with it easily. A distinction in the implementations of Data Mapper is in their scope. A Data Mapper can be specific to a particular Entity/Aggregate Root (single class or class with composed objects), or a generic implementation can be customized with metadata (annotations, XML configuration) to work with different classes. Generic implementations are usually very complex, and specific ones may become much more easy to code due to simplifications. However, generic Data Mappers are prone to reuse and present less bugs than the project-specific ones, which were the only alternative in the last years. Issues The difficulties in implementing such a pattern are clear. Given a transaction, like an http request, the mapper has to keep track of the changed objects, and generate automatically the right DML queries to issue (SELECT, UPDATE, DELETE), in the right order and without leaving out any part of the modified data, avoid duplicating rows or update ones that do not exist anymore. This is a case of simple interface and complex implementation. To avoid breaking encapsulation, implementations usually employ reflection to access private fields of the object to store or that the mapper is reconstituting. Other possible choices for the data access are specific constructors for reconstitution or specific interfaces for domain mapping, but this solution still breaks encapsulation by providing to the client code methods that are not meant to be called, or fields that should not even be seen out of the objects but are actually accessed. This results in a unclear Api which may promote dependencies on persistence-related items. Providing metadata breaks encapsulation too, of course, but at least it is kept in the immediate so that it can change with the domain classes. Annotations are the preferred mean to specify metadata such as column names or relationships, and in PHP they are hidden in the docblock comments so that when the Data Mapper is not used they are just ignored. Data Mapper does not provide a total illusion (abstraction) of an in-memory collection of objects: the knowledge that there is some kind of external data store scatters into the application upper layers. Moreover, eventually some particular issue of the storage leaks into the object part of the application. As an example, consider the performance of queries, which is often the object of discussion when using object-relational mappers. Usually not all the object graph is instantiated as it may be very large; tuning how large the instantiated part will be is a trade-off which depends on the underlying database. Furthermore, generated queries may result very inefficient to the point that much of the client code must hint the joins to perform via the Api. Examples The generic Data Mapper Doctrine 2 (now in beta) is one of the few implementations in PHP of this pattern. As we've seen before in this article, specific implementations are dependent on the domain layer, so they are usually not reusable. A working copy of Doctrine 2 would be too large in size for inclusion in this post, so we are only analyzing the interface that most of the client code would see: the Entity Manager (name borrowed from Hibernate and JPA, since Java application used Data Mappers for years before this pattern has seen adoption from PHP ones.) The Entity Manager is not a domain specific interface, but other patterns like the Repository one can then compose the mapper to provid segregated interfaces for a particular class (aggregate root). As always, I have removed the less interesting methods or code to show the Api, and expanded the comments. * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel */ class EntityManager { /** * Flushes all changes to objects that have been queued up to now to the database. * This effectively synchronizes the in-memory state of managed objects with the * database. * No query is executed before this method is called from client code. * * @throws Doctrine\ORM\OptimisticLockException If a version check on an entity that * makes use of optimistic locking fails. */ public function flush() { $this->_errorIfClosed(); $this->_unitOfWork->commit(); } /** * Finds an Entity by its identifier. * This method is often combined with query-oriented ones. * * @param string $entityName the class name * @param mixed $identifier usually primary key * @param int $lockMode * @param int $lockVersion * @return object */ public function find($entityName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null) { return $this->getRepository($entityName)->find($identifier, $lockMode, $lockVersion); } /** * Tells the EntityManager to make an instance managed and persistent. * * The entity will be entered into the database at or before transaction * commit or as a result of the flush operation. * * NOTE: The persist operation always considers entities that are not yet known to * this EntityManager as NEW. Do not pass detached entities to the persist operation. * * @param object $object The instance to make managed and persistent. */ public function persist($entity) { if ( ! is_object($entity)) { throw new \InvalidArgumentException(gettype($entity)); } $this->_errorIfClosed(); $this->_unitOfWork->persist($entity); } /** * Removes an entity instance. * * A removed entity will be removed from the database at or before transaction commit * or as a result of the flush operation. * * @param object $entity The entity instance to remove. */ public function remove($entity) { if ( ! is_object($entity)) { throw new \InvalidArgumentException(gettype($entity)); } $this->_errorIfClosed(); $this->_unitOfWork->remove($entity); } /** * Refreshes the persistent state of an entity from the database, * overriding any local changes that have not yet been persisted. * * @param object $entity The entity to refresh. */ public function refresh($entity) { if ( ! is_object($entity)) { throw new \InvalidArgumentException(gettype($entity)); } $this->_errorIfClosed(); $this->_unitOfWork->refresh($entity); } /** * Determines whether an entity instance is managed in this EntityManager. * * @param object $entity * @return boolean TRUE if this EntityManager currently manages the given entity, FALSE otherwise. */ public function contains($entity) { return $this->_unitOfWork->isScheduledForInsert($entity) || $this->_unitOfWork->isInIdentityMap($entity) && ! $this->_unitOfWork->isScheduledForDelete($entity); } /** * Factory method to create EntityManager instances. * * @param mixed $conn An array with the connection parameters or an existing * Connection instance. * @param Configuration $config The Configuration instance to use. * @param EventManager $eventManager The EventManager instance to use. * @return EntityManager The created EntityManager. */ public static function create($conn, Configuration $config, EventManager $eventManager = null); }
May 17, 2010
by Giorgio Sironi
· 10,028 Views
article thumbnail
Eclipse Profile Configuration: The Launch Requires at Least One Data Collector
I just installed TPTP into my Eclipse 3.5 under Ubuntu 9.04 and tried to profile a class. The Profile Configuration opened with a red warning reading “the launch requires at least one data collector to be selected“. Clicking the configuration’s Monitor tab reveals a more detailed error (and nothing to select): IWATO435E An error occured when connecting to the host. A quick check of the error log (Window – Show View – Other… – General – Error Log) reveals the cause: RAServer generated the following output: [Error Stream]:ACServer: error while loading shared libraries: /home/jholy/development/tools/eclipse-ide/pulse2-2.4.2/Common/plugins/org.eclipse.tptp.platform.ac.linux_ia32_4.4.202.v201002100300/agent_controller/bin/../lib/libtptpUtils.so.4: file too short Checking the content of the lib/ folder revealed an interesting thing: -rw-r–r– 1 jholy jholy 17 2010-02-16 23:16 libtptpUtils.so -rw-r–r– 1 jholy jholy 21 2010-02-16 23:16 libtptpUtils.so.4 -rwxr-xr-x 1 jholy jholy 100K 2010-02-16 23:16 libtptpUtils.so.4.5.0 As also the content of the two small files suggests (they contain a name of the corresponding file with a longer name), the *.so and *.so.4 files should have been links but the installer failed to create them. Solution List all files in the lib/ folder, you will see that there are many real files like libtptpUtils.so.4.5.0 and libxerces-c.so.26.0 and many should-be-links files. The solution is, of course, to replace all those files that shoud be links with actual links. For me the solution was: $ cd .../plugins/org.eclipse.tptp.platform.ac.linux_ia32_4.4.202.v201002100300/agent_controller/lib # Move out the files that are OK lib$ mkdir tmp lib$ mv libswt-* libcbe.so tmp/ # Fix the links lib$ for FILE in `ls *.so`; do ln -sf "${FILE}.4.5.0" $FILE; ln -sf "${FILE}.4.5.0" "${FILE}.4"; done # Move the correct files back lib$ mv tmp/* . lib$ rmdir tmp # Fix links for files with *.26 instead of *.4.5.0 lib$ ln -sf libxerces-c.so.26.0 libxerces-c.so.26 lib$ ln -sf libxerces-c.so.26.0 libxerces-c.so lib$ ln -sf libxerces-depdom.so.26.0 libxerces-depdom.so.26 lib$ ln -sf libxerces-depdom.so.26.0 libxerces-depdom.so lib$ rm libxerces-depdom.so.4 libxerces-c.so.4 # Done! Try to open the profile configuration now, the IWATO435E should have disappeared and you should be able to select a data collector. If not, restart Eclipse, try again, check the error log. My environment Ubuntu 9.04 Eclipse 3.5 TPTP – see above From http://theholyjava.wordpress.com/2010/05/13/eclipse-profile-configuration-the-launch-requires-at-least-one-data-collector/
May 14, 2010
by Jakub Holý
· 13,233 Views
article thumbnail
Practical PHP Patterns: Active Record
The Active Record pattern effectively prescribes to wrap a row of a database table in a domain object with a 1:1 relationship, managing its state and adding business logic in the wrapping class code. An Active Record implementation is in fact a classical C structure aka Record aka associative array of data, with the addition of utility methods that encapsulate behavior that acts on these data. The most useful method is usually the save() one, which updates the database reflecting in the row the current state of the record. Thus, the Active Record transparently works with SQL queries and provides an higher-level Api. Although Active Record is similar in implementation to the Row Data Gateway pattern, it is distinguished from it in the fact that it defines methods with domain-specific logic. The consequence of the presence of domain-specific logic is that generic implementations of Active Record provided by libraries must be customized to met the need of the object model. Typically this customization is done with a thin subclassing, which at least renames the library class with a domain name (like User or Post) and may specify metadata on the database table where the Active Records state is kept, if they are not inferred. The issue of subclassing Subclassing allows the developer to create new methods and properties to represent business logic, and to build a richer and more specific interface than the one constituted by simple Row Data Gateway objects. Despite these advantage, this interface is not much segregated, as subclassing exposes all the public methods of the base library class, on which the developers of the domain model have no control. Subclassing also ties the domain layer to the infrastructure one, being it a library or a framework or every kind of data persistence layer (examples of PHP ORMs that use Active Record are the Zend_Db component, Doctrine 1 and Propel). Domain objects cannot be created or even their class source code loaded without having the library code available. This is an issue when reusing the model in a different environment, and even in test suites. If the library is powerful enough, it may provide adapters for different databases so that a lightweight database instance can be created in the testing environment. Another caveat of Active Record is the fundamental assumption that a domain entity is always a row of a table of a relational database; this constraint is forced even when it is not appropriate, and the database and object model must match. In fact, part of the database (like foreign keys) often scatter into the domain model, as an Active Record with an external one-to-one relationship will usually store not only the related object but also its foreign key. Another example of mirroring of the relational model into the object graph is for the management of M-to-N associating entities, often forced to become real entities even when they do not make sense (the famous UserGroup classes that tie together User and Group rows). Diffusion Thus, the Active Record pattern puts at risk the freedom of implementing a powerful Domain Model, where the object graph is a mix of state-carrying and behavior-carrying objects, like Strategies and Specifications. It is however, a radical simplification in implementation of domain models where CRUD functions are all the rage, and there is no gain in implementing objects that do not simply map to a relational database. Note that in the case of PHP, most of the custom web applications developed in this language are deeply influenced by the back end, assumed as a relational database or even as MySQL. But while the technologies for user-to-application and application-to.application interaction on the web continue to grow, the situation will continue to evolve and if PHP wants to keep up with the pace of other dynamic languages, Java and .NET, it needs to finally decouple from the relational database as the unique model of data. By the way, current implementation of persistence frameworks are transitioning towards a Data Mapper approach (not only in PHP but also in Ruby, while Java has done that years ago with Hibernate and JPA), which is less invasive on the Domain Model source code, and does not introduce an hard dependency from the domain layer to infrastructure components. Examples This sample implementation of the Active Record pattern is taken from the Doctrine 1.2 ORM. The base class in this framework id Doctrine_Record (together with Doctrine_Record_Abstract), while a base class with the schema metadata is regenerated from a model, and it is subclassed for orthogonality of customization and synchronization by the developer. The base Active Record class looks like this: /** * Implements also __get() and __set(), not shown along with many other dozen methods. */ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Countable, IteratorAggregate, Serializable { /** * Empty template method to provide concrete Record classes with the possibility * to hook into the saving procedure. */ public function preSave($event) { } /** * Empty template method to provide concrete Record classes with the possibility * to hook into the saving procedure. */ public function postSave($event) { } /** * applies the changes made to this object into database * this method is smart enough to know if any changes are made * and whether to use INSERT or UPDATE statement * * this method also saves the related components * * @param Doctrine_Connection $conn optional connection parameter * @return void */ public function save(Doctrine_Connection $conn = null) { if ($conn === null) { $conn = $this->_table->getConnection(); } $conn->unitOfWork->saveGraph($this); } /** * returns a string representation of this object */ public function __toString() { return (string) $this->_oid; } } If we have an Article entity, it will be represented via subclassing of the generic Active Record. In Doctrine 1, a subclass can be generated by writing a compact Yaml model, or even reverse engineered from an existing database: /** * BaseOtk_Content_Article * * This class has been auto-generated by the Doctrine ORM Framework * * @property integer $id * @property integer $section_id * @property integer $author_id * @property integer $image_id * @property string $title * @property string $description * @property string $text * @property integer $visits * @property boolean $draft * @property boolean $closed * @property Otk_Content_Section $section * @property Otk_User $author * @property Otk_File $image * @property Doctrine_Collection $sections * @property Doctrine_Collection $Otk_Content_Tag * @property Doctrine_Collection $comments * */ abstract class BaseOtk_Content_Article extends Otk_Model_Record { public function setTableDefinition() { $this->setTableName('oss_content_articles'); $this->hasColumn('id', 'integer', 3, array('type' => 'integer', 'primary' => true, 'autoincrement' => true, 'length' => '3')); $this->hasColumn('section_id', 'integer', 2, array('type' => 'integer', 'notnull' => true, 'length' => '2')); $this->hasColumn('author_id', 'integer', 3, array('type' => 'integer', 'length' => '3')); $this->hasColumn('image_id', 'integer', 3, array('type' => 'integer', 'length' => '3')); $this->hasColumn('title', 'string', 255, array('type' => 'string', 'length' => '255')); $this->hasColumn('description', 'string', 1000, array('type' => 'string', 'length' => '1000')); $this->hasColumn('text', 'string', null, array('type' => 'string')); $this->hasColumn('visits', 'integer', 3, array('type' => 'integer', 'length' => '3')); $this->hasColumn('draft', 'boolean', null, array('type' => 'boolean', 'notnull' => true, 'default' => false)); $this->hasColumn('closed', 'boolean', null, array('type' => 'boolean')); } public function setUp() { $this->hasOne('Otk_Content_Section as section', array('local' => 'section_id', 'foreign' => 'id')); $this->hasOne('Otk_User as author', array('local' => 'author_id', 'foreign' => 'id')); $this->hasOne('Otk_File as image', array('local' => 'image_id', 'foreign' => 'id')); $this->hasMany('Otk_Content_Section as sections', array('refClass' => 'Otk_Content_Tag', 'local' => 'article_id', 'foreign' => 'section_id')); $this->hasMany('Otk_Content_Tag', array('local' => 'id', 'foreign' => 'article_id')); $this->hasMany('Otk_Content_Comment as comments', array('local' => 'id', 'foreign' => 'article_id')); $timestampable0 = new Doctrine_Template_Timestampable(); $sluggable0 = new Doctrine_Template_Sluggable(array('fields' => array(0 => 'title'), 'canUpdate' => true, 'unique' => true)); $this->actAs($timestampable0); $this->actAs($sluggable0); } } To support further regenerations of the subclass as the schema evolves, another subclassing step is necessary. This class will never be touched by the regeneration process and it is the one referred in client code. /** * This class defines the domain logic via addition of methods. */ class Otk_Content_Article extends BaseOtk_Content_Article { public function getTags() { $tags = array(); foreach ($this->sections as $section) { $tags[$section->slug] = $section->name; } return $tags; } }
May 12, 2010
by Giorgio Sironi
· 8,964 Views
article thumbnail
A Look Inside JBoss Microcontainer - The Scanning Library
Today's JEE doesn't require configuration files any more. Most of the configuration, if not all, is done over properly annotated classes. As such, it's the responsibility of the underlying containers to find these annotations and act accordingly. At a first glance it appears that there is no other way for a container to implement that than to fully scan a given deployment. And we all know this can be very time consuming, especially if there are multiple container components that require this information, and have no integration hooks available to get to a container's already gathered information. From requirements collected here, I've introduced a new MC Scanning sub-project. The main goal or idea behind this lib is very simple: unify all of JBossAS component scanning into a single-pass scan. Instead of doing the resource scanning for every component, we just do it once, properly delegating the work to various container components. Another goal was to also enable usage of pre-indexed information, so that there would actually be no need for the scanning itself - e.g. one could pre-index all of jar's annotations during the build. Read the other parts in DZone's exclusive JBoss Microcontainer Series: Part 1 -- Component Models Part 2 –- Advanced Dependency Injection and IoC Part 3 -- the Virtual File System Project structure scanning-spi - Contains a simple scanner, metadata SPI, and initial helpers to help you extend / use a simple version of this lib. scanning-impl - Provides component agnostic scanning API. It also includes generic metadata implementation and its usage. plugins - This module holds custom component-scanning implementations. Current implementations are: Annotations Hibernate Hierarchy JSF Web Weld deployers - Integration with VDF; new custom deployers. indexer - This module contains utils for creating pre-indexed handles, and merging them into existing jars. It includes Ant task and Maven plugin. testsuite - Tests for all other modules. Basic building blocks The org.jboss.scanning.spi.Scanner class is the most abstract - most basic interface to interact with any scanner implementation. It only has scan() method. For any really useful operation one will have to use some concrete implementation's constructors, properties ... and then use scan() to trigger the scan operation. The main interface of interest for us is org.jboss.scanning.spi.ScanningPlugin: package org.jboss.scanning.spi; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.jboss.classloading.spi.visitor.ResourceFilter; import org.jboss.classloading.spi.visitor.ResourceVisitor; /** * Scanning plugin. * Defines what to do with a resource. * * @param exact handle type * @param exact handle interface * @author Ales Justin */ public interface ScanningPlugin extends ResourceFilter, ResourceVisitor { /** * Create plugins handle/utility. * e.g. AnnotationRepository for annotations scanning * * @return new handle instance */ T createHandle(); /** * Read serialized handle. * * @param is the serialized handle's input stream. * @return de-serialized handle * @throws Exception for any error */ ScanningHandle readHandle(InputStream is) throws Exception; /** * Write / serialize handle. * * @param os the output stream to serialize handle. * @param handle the handle * @throws IOException for any IO error */ void writeHandle(OutputStream os, T handle) throws IOException; /** * Cleanup handle. * * @param handle the handle to cleanup */ void cleanupHandle(U handle); /** * Get handle interface. * * @return the handle interface */ Class getHandleInterface(); /** * Get handle's key. * Used to attach handle to map/attachments. * * @return the handle's key */ String getAttachmentKey(); /** * Get handle's file name. * Used to attach handle to jar and/or get pre-indexed. * * @return the handle's file name */ String getFileName(); /*** * Get recurse filter. * * @return the recurse filter */ ResourceFilter getRecurseFilter(); } Most of the functionality is already implemented in its abstract form (AbstractScanningPlugin) so you only need to provide the custom logic. As you can already see from the plugin's signature, the plugin introduces a handle. A handle is what will hold the scanning information for particular component; e.g. an annotation repository. We can see handle's implementation defined as parameter T, where handle's interface is parameter U. /** * Scanning handle. * * Represents a simple interface resource scanning results must implement * in order to be able to merge pre-existing results. * * @param exact handle type * @author Ales Justin */ public interface ScanningHandle { /** * Merge existing handle with sub-handle / pre-existing handle. * * @param subHandle the sub handle */ void merge(T subHandle); } The main purpose of handle introduction is to allow for pre-existing handle's merging in type safe manner. How to make usage of plugins as easy as possible in MC? As we can see Scanner (or its actual implementations) takes a set of plugins to handle artifacts. But since plugins are mostly mutable, we need some sort of factory to help use create these plugins. For VDF based usage this is how our factory looks like: import org.jboss.deployers.structure.spi.DeploymentUnit; import org.jboss.scanning.spi.ScanningHandle; import org.jboss.scanning.spi.ScanningPlugin; /** * Deployment based scanning plugin factory. * Used for incallback automatching. * * @param exact handle type * @author Ales Justin */ public interface DeploymentScanningPluginFactory { /** * Is this plugin relevant to unit. * * @param unit the unit to check against * @return true if it's relevant, false otherwise */ boolean isRelevant(DeploymentUnit unit); /** * Create scanning plugin from deployment unit. * * @param unit the deployment unit * @return new scanning plugin */ ScanningPlugin create(DeploymentUnit unit); } Also, as the javadoc says, this interface is nicely used for MC's incallback usage (incallback is a kind of dependency injection where one component can insert itself into another via a method call, as explained in one of the previous articles on JBoss Microcontainer). Usage example Let's see what we need to implement in order to get annotation scanning into the repository. public class AnnotationsScanningPluginFactory implements DeploymentScanningPluginFactory { public boolean isRelevant(DeploymentUnit unit) { // any better check? -- metadata complete is already done elsewhere // see JBossMetaDataDeploymentUnitFilter in JBossAS return true; } public ScanningPlugin create(DeploymentUnit unit) { ReflectProvider provider = DeploymentUtilsFactory.getProvider(unit); ResourceOwnerFinder finder = DeploymentUtilsFactory.getFinder(unit); return new AnnotationsScanningPlugin(provider, finder, unit.getClassLoader()); } } public class AnnotationsScanningPlugin extends AbstractClassLoadingScanningPlugin { /** The repository */ private final DefaultAnnotationRepository repository; /** The visitor */ private final ResourceVisitor visitor; public AnnotationsScanningPlugin(ClassLoader cl) { this(IntrospectionReflectProvider.INSTANCE, ClassResourceOwnerFinder.INSTANCE, cl); } public AnnotationsScanningPlugin(ReflectProvider provider, ResourceOwnerFinder finder, ClassLoader cl) { repository = new DefaultAnnotationRepository(cl); visitor = new GenericAnnotationVisitor(provider, finder, repository); } protected DefaultAnnotationRepository doCreateHandle() { return repository; } protected ClassLoader getClassLoader() { return repository.getClassLoader(); } @Override public void cleanupHandle(AnnotationIndex handle) { if (handle instanceof DefaultAnnotationRepository) DefaultAnnotationRepository.class.cast(handle).cleanup(); } public Class getHandleInterface() { return AnnotationIndex.class; } public ResourceFilter getFilter() { return visitor.getFilter(); } public void visit(ResourceContext resource) { visitor.visit(resource); } } public class GenericAnnotationVisitor extends ClassHierarchyResourceVisitor { /** The mutable repository */ private MutableAnnotationRepository repository; public GenericAnnotationVisitor(ReflectProvider provider, ResourceOwnerFinder finder, MutableAnnotationRepository repository) { super(provider, finder); if (repository == null) throw new IllegalArgumentException("Null repository"); this.repository = repository; } protected boolean isRelevant(ClassInfo classInfo) { return repository.isAlreadyChecked(classInfo.getName()) == false; } public ResourceFilter getFilter() { return ClassFilter.INSTANCE; } @Override protected void handleAnnotations(ElementType type, Signature signature, Annotation[] annotations, String className, URL ownerURL) { if (annotations != null && annotations.length > 0) { for (Annotation annotation : annotations) { repository.putAnnotation(annotation, type, className, signature, ownerURL); } } } } While the repository is just a-bit-smarter Map. Integration with VDF Using the Indexer public class Main { private static final Logger log = Logger.getLogger(Main.class.getName()); /** * Usage */ private static void usage() { System.out.println("Usage: Indexer "); } /** * Main. * The output is file named .jar.mcs. * * @param args the program arguments */ public static void main(String[] args) { try { int offset = 2; if (args.length < offset) { File input = new File(args[0]); String[] providers = args[1].split(","); URL[] urls = new URL[args.length - offset]; // add the rest of classpath for (int i = 0; i < urls.length; i++) urls[i] = new File(args[i + offset]).toURI().toURL(); ScanUtils.scan(input, Constants.applyAliases(providers), urls); } else { usage(); } } catch (Throwable t) { log.log(Level.SEVERE, t.getMessage(), t); } } } Pre-existing or pre-indexed information For each scanning plugin we look for artifact's META-INF/ entry. String fileName = plugin.getFileName(); for (URL root : roots) { InputStream is = getInputStream(root, Scanner.META_INF + fileName); if (is != null) { ScanningHandle pre = plugin.readHandle(is); handle.merge(pre); It's plugin's responsibility to know how to read pre-existing handle. By default we use plain Java serialization together with gzip. public ScanningHandle readHandle(InputStream is) throws Exception { try { GZIPInputStream gis = new GZIPInputStream(is); ObjectInputStream ois = createObjectInputStream(gis); return (ScanningHandle) ois.readObject(); } finally { is.close(); } } public void writeHandle(OutputStream os, T handle) throws IOException { GZIPOutputStream gos = new GZIPOutputStream(os); ObjectOutputStream oos = new ObjectOutputStream(gos); try { oos.writeObject(handle); oos.flush(); } finally { oos.close(); } } How to limit scanning? There already was a jboss-scanning.xml, I've just enhanced it a bit. The recurse filter is now a bit smarter, and consequently faster, than it used to be in previous version. package org.jboss.scanning.plugins.filter; import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.jboss.classloading.spi.visitor.ResourceContext; import org.jboss.classloading.spi.visitor.ResourceFilter; import org.jboss.scanning.spi.metadata.PathEntryMetaData; import org.jboss.scanning.spi.metadata.PathMetaData; import org.jboss.scanning.spi.metadata.ScanningMetaData; import org.jboss.vfs.util.PathTokenizer; /** * Simple recurse filter. * * It searches for path substring in url string, * and tries to match the tree structure as far as it goes. */ public class ScanningMetaDataRecurseFilter implements ResourceFilter { /** Path tree roots */ private Map roots; public ScanningMetaDataRecurseFilter(ScanningMetaData smd) { if (smd == null) throw new IllegalArgumentException("Null metadata"); List paths = smd.getPaths(); if (paths != null && paths.isEmpty() == false) { roots = new HashMap(); for (PathMetaData pmd : paths) { RootNode pathNode = new RootNode(); roots.put(pmd.getPathName(), pathNode); Set includes = pmd.getIncludes(); if (includes != null && includes.isEmpty() == false) { pathNode.explicitInclude = true; for (PathEntryMetaData pemd : includes) { String name = pemd.getName(); String[] tokens = name.split("\\."); Node current = pathNode; for (String token : tokens) current = current.addChild(token); if (pemd.isRecurse()) current.recurse = true; // mark last one as recurse } } } } } public boolean accepts(ResourceContext resource) { if (roots == null) return false; URL url = resource.getUrl(); String urlString = url.toExternalForm(); for (Map.Entry root : roots.entrySet()) { if (urlString.contains(root.getKey())) { RootNode rootNode = root.getValue(); if (rootNode.explicitInclude) // we have explicit includes in path, try tree path { String resourceName = resource.getResourceName(); List tokens = PathTokenizer.getTokens(resourceName); Node current = rootNode; // let's try to walk some tree path for (String token : tokens) { // if we're here, the rest is recursively matched if (current.recurse) break; current = current.getChild(token); // no fwd path if (current == null) return false; } } return true; } } return false; } private static class Node { private Map children; private boolean recurse; public Node addChild(String value) { if (children == null) children = new HashMap(); Node child = children.get(value); if (child == null) { child = new Node(); children.put(value, child); } return child; } public Node getChild(String child) { return children != null ? children.get(child) : null; } } private static class RootNode extends Node { private boolean explicitInclude; } } Javassist based JBoss Reflect In order to avoid loading the actual resource's underlying class, we use Javassist under the hood - via JBoss Refect project. DeploymentUnit unit = assertDeploy(jar); try { TIFScanningPlugin plugin = unit.getAttachment(TIFScanningPlugin.class); assertNotNull(plugin); Kernel kernel = assertBean("Kernel", Kernel.class); KernelConfigurator configurator = kernel.getConfigurator(); ClassLoader cl = unit.getClassLoader(); String name = JarMarkOnClass.class.getName(); TypeInfo ti = configurator.getTypeInfo(name, cl); TypeInfo visited = plugin.getResources().get(name); assertSame(ti, visited); // let's check if the cache is working Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class); findLoadedClass.setAccessible(true); Object clazz = findLoadedClass.invoke(cl, name); assertNull(clazz); // should not be loaded } finally { undeploy(unit); } But the overall usage of helper utils is pluggable: /** * Find the util for deployment. * Newly created utils are grouped per module. * * @author Ales Justin */ public class DeploymentUtilsFactory { /** The default impls */ private static Map, UtilFactory> defaults = new WeakHashMap, UtilFactory>(); static { addImplementation(ReflectProvider.class, new ReflectProviderUtilFactory()); addImplementation(ResourceOwnerFinder.class, new ResourceOwnerFinderUtilFactory()); } /** * Add the util impl. * * @param iface the interface * @param factory the util factory */ public static void addImplementation(Class iface, UtilFactory factory) { defaults.put(iface, factory); } /** * Remove the util impl. * * @param iface the interface */ public static void removeImplementation(Class iface) { defaults.remove(iface); } /** * Get util. * * @param unit the deployment unit * @param utilType the util type * @return util instance */ public static T getUtil(DeploymentUnit unit, Class utilType) { if (utilType == null) throw new IllegalArgumentException("Null util type"); DeploymentUnit moduleUnit = getModuleUnit(unit); T util = moduleUnit.getAttachment(utilType); if (util == null) { UtilFactory factory = defaults.get(utilType); if (factory == null) throw new IllegalArgumentException("No util factory defined for " + utilType); Object instance = factory.create(moduleUnit); util = utilType.cast(instance); moduleUnit.addAttachment(utilType, util); } return util; } /** * Get module unit. * * @param unit the current unit * @return unit containing Module, or exception if no such unit exists */ public static DeploymentUnit getModuleUnit(DeploymentUnit unit) { if (unit == null) throw new IllegalArgumentException("Null unit"); // group util per module DeploymentUnit moduleUnit = unit; while(moduleUnit != null && moduleUnit.isAttachmentPresent(Module.class) == false) moduleUnit = moduleUnit.getParent(); if (moduleUnit == null) throw new IllegalArgumentException("No module in unit: " + unit); return moduleUnit; } /** * Wrap util lookup in lazy lookup. * * @param unit the deployment unit * @param utilType the util type * @return lazy util proxy */ public static T getLazyUtilProxy(DeploymentUnit unit, Class utilType) { // null check is in handler LazyUtilsProxyHandler handler = new LazyUtilsProxyHandler(unit, utilType); Object proxy = Proxy.newProxyInstance(unit.getClassLoader(), new Class[]{utilType}, handler); return utilType.cast(proxy); } /** * Get reflect provider. * * @param unit the depoyment unit * @return the provider */ public static ReflectProvider getProvider(DeploymentUnit unit) { return getUtil(unit, ReflectProvider.class); } /** * Get finder. * * @param unit the depoyment unit * @return the finder */ public static ResourceOwnerFinder getFinder(DeploymentUnit unit) { return getUtil(unit, ResourceOwnerFinder.class); } /** * Cleanup the util. * * @param util the util to cleanup */ public static void cleanup(Object util) { if (util instanceof CachingResourceOwnerFinder) CachingResourceOwnerFinder.class.cast(util).cleanup(); } } Meaning it's easy to swap utils behavior for particular deployment unit. e.g. different ResourceOwnerFinder or ReflectProvider Reporting issues As usual, use the forums: MC user forum MC dev forum In my previous article I've actually promised an article on our current native OSGi framework work, but since I was heavily into writing this new scanning lib I though I could share my thoughts / ideas on it while they were still hot. Specially with scanning being a constantly present topic in today's enterprise usage. One thing to note here - all of this is still at prototype stage, with no real release yet, although the main concepts have been in the making for a while, from initial support in VDF, to custom Papaki library, Scannotations, ... hence they've grown from experience. But this doesn't mean feedback is not welcome. :-) I'll be trying to fulfill my promise next time with an OSGi article, unless our Reflect gets the best of me. ;-) P.S.: As usual, again thanks to Marko for doing the editing of this article. About the Author Ales Justin was born in Ljubljana, Slovenia and graduated with a degree in mathematics from the University of Ljubljana. He fell in love with Java eight years ago and has spent most of his time developing information systems, ranging from customer service to energy management. He joined JBoss in 2006 to work full time on the Microcontainer project, currently serving as its lead. He also contributes to JBoss AS and is Seam, Weld and Spring integration specialist. He represent JBoss on 'OSGi' expert groups.
May 11, 2010
by Ales Justin
· 24,154 Views
article thumbnail
Interpreter Pattern Tutorial with Java Examples
Learn the Interpreter Design Pattern with easy Java source code examples as James Sugrue continues his design patterns tutorial series, Design Patterns Uncovered
May 11, 2010
by James Sugrue
· 54,657 Views
article thumbnail
The Power of Proxies in Java
In this article, I’ll show you the path that leads to true Java power, the use of proxies. They are everywhere but only a handful of people know about them. Hibernate for lazy loading entities, Spring for AOP, LambdaJ for DSL, only to name a few: they all use their hidden magic. What are they? They are… Java’s dynamic proxies. Everyone knows about the GOFProxy design pattern: Allows for object level access control by acting as a pass through entity or a placeholder object. Likewise, in Java, a dynamic proxy is an instance that acts as a pass through to the real object. This powerful pattern let you change the real behaviour from a caller point of view since method calls can be intercepted by the proxy. Pure Java proxies Pure Java proxies have some interesting properties: They are based on runtime implementations of interfaces They are public, final and not abstract They extend java.lang.reflect.Proxy In Java, the proxy itself is not as important as the proxy’s behaviour. The latter is done in an implementation of java.lang.reflect.InvocationHandler. It has only a single method to implement: public Object invoke(Object proxy, Method method, Object[] args) proxy: the proxy instance that the method was invoked on method: the Method instance corresponding to the interface method invoked on the proxy instance. The declaring class of the Method object will be the interface that the method was declared in, which may be a superinterface of the proxy interface that the proxy class inherits the method through args: an array of objects containing the values of the arguments passed in the method invocation on the proxy instance, or null if interface method takes no arguments. Arguments of primitive types are wrapped in instances of the appropriate primitive wrapper class, such as java.lang.Integer or java.lang.Boolean Let’s take a simple example: suppose we want a List that can’t be added elements to it. The first step is to create the invocation handler: public class NoOpAddInvocationHandler implements InvocationHandler { private final List proxied; public NoOpAddInvocationHandler(List proxied) { this.proxied = proxied; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().startsWith("add")) { return false; } return method.invoke(proxied, args); } } The invoke method will intercept method calls and do nothing if the method starts with “add”. Otherwise, it will the call pass to the real proxied object. This is a very crude example but is enough to let us understand the magic behind. Notice that in case you want your method call to pass through, you need to call the method on the real object. For this, you’ll need a reference to the latter, something the invoke method does not provide. That’s why in most cases, it’s a good idea to pass it to the constructor and store it as an attribute. Note: under no circumstances should you call the method on the proxy itself since it will be intercepted again by the invocation handler and you will be faced with a StackOverflowError. To create the proxy itself: List proxy = (List) Proxy.newProxyInstance( NoOpAddInvocationHandlerTest.class.getClassLoader(), new Class[] { List.class }, new NoOpAddInvocationHandler(list)); The newProxyInstance method takes 3 arguments: the class loader an array of interfaces that will be implemented by the proxy the power behind the throne in the form of the invocation handler Now, if you try to add elements to the proxy by calling any add methods, it won’t have any effect. CGLib proxies Java proxies are runtime implementations of interfaces. Objects do not necessarily implement interfaces, and collections of objects do not necessarily share the same interfaces. Confronted with such needs, Java proxies fail to provide an answser. Here begins the realm of CGLib. CGlib is a third-party framework, based on bytecode manipulation provided by ASM that can help with the previous limitations. A word of advice first, CGLib’s documentation is not on par with its features: there’s no tutorial nor documentation. A handful of JavaDocs is all you can count on. This said CGLib waives many limitations enforced by pure Java proxies: you are not required to implement interfaces you can extend a class For example, since Hibernate entities are POJO, Java proxies cannot be used in lazy-loading; CGLib proxies can. There are matches between pure Java proxies and CGLib proxies: where you use Proxy, you use net.sf.cglib.proxy.Enhancer class, where you use InvocationHandler, you use net.sf.cglib.proxy.Callback. The two main differences is that Enhancer has a public constructor and Callback cannot be used as such but only through one of its subinterfaces: Dispatcher: Dispatching Enhancer callback FixedValue: Enhancer callback that simply returns the value to return from the proxied method LazyLoader: Lazy-loading Enhancer callback MethodInterceptor: General-purpose Enhancer callback which provides for “around advice” NoOp: Methods using this Enhancer callback will delegate directly to the default (super) implementation in the base class As an introductory example, let’s create a proxy that returns the same value for hash code whatever the real object behind. The feature looks like a MethodInterceptor, so let’s implement it as such:
May 11, 2010
by Nicolas Fränkel
· 153,554 Views · 1 Like
article thumbnail
Concurrent JUnit Tests With RunnerScheduler
JUnit has a very cool feature called RunnerScheduler. A custom RunnerScheduler can be set on a ParentRunner to control how child elements are executed. If you are on a Suite, the child elements would be each test class. If you are on a simple class (Junit4 runner) the child elements are all the test methods. Thus, with a RunnerScheduler you are able to control the overall execution of your test flow. As an example, suppose you want to execute your test methods concurrently on a given test. You could have a runner called ConcurrentJunitRunner. @RunWith(ConcurrentJunitRunner.class) @Concurrent(threads = 6) public final class ATest { @Test public void test0() throws Throwable { printAndWait(); } @Test public void test1() throws Throwable { printAndWait(); } @Test public void test2() throws Throwable { printAndWait(); } @Test public void test3() throws Throwable { printAndWait(); } @Test public void test4() throws Throwable { printAndWait(); } @Test public void test5() throws Throwable { printAndWait(); } @Test public void test6() throws Throwable { printAndWait(); } @Test public void test7() throws Throwable { printAndWait(); } @Test public void test8() throws Throwable { printAndWait(); } @Test public void test9() throws Throwable { printAndWait(); } void printAndWait() throws Throwable { int w = new Random().nextInt(1000); System.out.println(String.format("[%s] %s %s %s", Thread.currentThread().getName(), getClass().getName(), new Throwable().getStackTrace()[1].getMethodName(), w)); Thread.sleep(w); } } The @Concurrent annotation controls the thread count. The runner implements a custom RunnerScheduler which delegates to a thread pool and Java Concurrent API each test method. Thus all test are executed concurrently and the RunnerScheduler waits for all tests to finish. But wait ! There's even more ! This runner just makes the test methods of a class runnable concurrently. But if you have a lot of tests in your project, you would probably want to also run all these tests concurrently ! Here come the ConcurrentSuite runner ! @RunWith(ConcurrentSuite.class) @Suite.SuiteClasses({ATest.class, ATest2.class, ATest3.class}) public class MySuite { } This runner will run all the tests in your suite. If a test class uses the ConcurrentJunitRunner or is annotated by @Concurrent then its method will be run concurrently. Otherwise it will be run sequentially. The runners provided on this article demonstrates how to use a custom RunnerScheduler, but can be safely used in any projects and be modified according to your needs. All the code for this article can be found here. You can also checkout the classes: svn co http://mycila.googlecode.com/svn/sandbox/ sandbox Mathieu Carbou http://blog.mycila.com/ http://www.junit.org/node/589
May 10, 2010
by Mathieu Carbou
· 39,437 Views · 2 Likes
article thumbnail
Two Ways to Convert Java Map to String
This article shows 2 ways to convert Java Map to String. Approach 1: simple, lightweight – produces query string like output, but restrictive. Approach 2: uses Java XML bean serialization, more robust but produces overly verbose output. Approach 1: Map to query string format Approach 1 converts a map to a query-string like output. Here’s what an output looks like: name1=value1&name2=value2 Full Code: import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; public class MapUtil { public static String mapToString(Map map) { StringBuilder stringBuilder = new StringBuilder(); for (String key : map.keySet()) { if (stringBuilder.length() > 0) { stringBuilder.append("&"); } String value = map.get(key); try { stringBuilder.append((key != null ? URLEncoder.encode(key, "UTF-8") : "")); stringBuilder.append("="); stringBuilder.append(value != null ? URLEncoder.encode(value, "UTF-8") : ""); } catch (UnsupportedEncodingException e) { throw new RuntimeException("This method requires UTF-8 encoding support", e); } } return stringBuilder.toString(); } public static Map stringToMap(String input) { Map map = new HashMap(); String[] nameValuePairs = input.split("&"); for (String nameValuePair : nameValuePairs) { String[] nameValue = nameValuePair.split("="); try { map.put(URLDecoder.decode(nameValue[0], "UTF-8"), nameValue.length > 1 ? URLDecoder.decode( nameValue[1], "UTF-8") : ""); } catch (UnsupportedEncodingException e) { throw new RuntimeException("This method requires UTF-8 encoding support", e); } } return map; } } Example usage code Map map = new HashMap(); map.put("color", "red"); map.put("symbols", "{,=&*?}"); map.put("empty", ""); String output = MapUtil.mapToString(map); Map parsedMap = MapUtil.stringToMap(output); for (String key : map.keySet()) { Assert.assertEquals(parsedMap.get(key), map.get(key)); } Output with Approach 1: symbols=%7B%2C%3D%26*%3F%7D&color=red∅= Caveat Only supports String keys and values. Due to the nature of serialization, null keys and values are not supported. Null will be converted to an empty String. This is because there is no way to distinguish between a null and an empty String in the serialized form. If you need support for null keys and values, use java.beans.XMLEncoder as shown below. Approach 2: Java Bean XMLEncoder: Map to String Java provides XMLEncoder and XMLDecoder classes as part of the java.beans package as a standard way to serialize and deserialize objects. This Map map = new HashMap(); map.put("color", "red"); map.put("symbols", "{,=&*?}"); map.put("empty", ""); ByteArrayOutputStream bos = new ByteArrayOutputStream(); XMLEncoder xmlEncoder = new XMLEncoder(bos); xmlEncoder.writeObject(map); xmlEncoder.flush(); String serializedMap = bos.toString() System.output.println(serializedMap); Output with Approach 2 The serialized value is shown below. As you can see this is more verbose, but can accommodate different data types and null keys and values. symbols {,=&*?} color red empty symbols {,=&*?} color red empty Java Bean XMLDecoder: String to Map XMLDecoder xmlDecoder = new XMLDecoder(new ByteArrayInputStream(serializedMap.getBytes())); Map parsedMap = (Map) xmlDecoder.readObject(); for (String key : map.keySet()) { Assert.assertEquals(parsedMap.get(key), map.get(key)); } Summary While Java provides a standard (and overly verbose) way to serialize and deserialize objects, this articles discusses an alternative lightweight way to convert a Java Map to String and back. If you are serializing a map with non-null String keys and values, then you should be able to use this alternative way, otherwise use the Java bean serialization. From http://www.vineetmanohar.com/2010/05/07/2-ways-to-convert-java-map-to-string
May 8, 2010
by Vineet Manohar
· 139,134 Views
article thumbnail
Finalization and Phantom References
Memory management is done automatically in Java. The programmer doesn't need to worry about reference objects that have been released. One downside to this approach is that the programmer cannot know when a particular object will be collected. Moreover, the programmer has no control over memory management. However, the java.lang.ref package defines classes that provide a limited degree of interaction with the garbage collector. The concrete classes SoftReference, WeakReferenceand PhantomReference are subclasses of Reference that interact with the garbage collector in different ways. In this article we will discuss the functionality and behavior of the PhantomReferenceclasses and see how it can be used. Problem with Finalization To perform some postmortem cleanup on objects that garbage collector consider as unreachable, one can use finalization. This feature can be utilized to reclaim native resources associated with an object. However, finalizers have many problems associated. Firstly, we can’t foresee the call of finalize(). Since the Garbage Collection is unpredictable, the calling of finalize() cannot be predicted. There is no guarantee that the object will be garbage collected. The object might never become eligible for GC because it could be reachable through the entire lifetime of the JVM. It is also possible that no garbage collection actually runs from the time the object became eligible and before JVM stops. Secondly, Finalization can slowdown an application. Managing objects with a finalize() method takes more resources from the JVM than normal objects. As per doc, You should also use finalization only when it is absolutely necessary. Finalization is a nondeterministic -- and sometimes unpredictable -- process. The less you rely on it, the smaller the impact it will have on the JVM and your application In Effective Java, 2nd ed., Joshua Bloch says, there is a severe performance penalty for using finalizers... So what should you do instead of writing a finalizer for a class whose objects encapsulate resources that require termination, such as files or threads? Just provide an explicit termination method, and require clients of the class to invoke this method on each instance when it is no longer needed. In short, Finalize() isn't used often, and also there is no much reason to use it. If we have a class with methods like close() or cleanup() and that should be called once user done with the object then placing these methods call in finalize() can be used as a safety measure, but not necessary. Phantom Reference phantom reachable, phantomly reachable An object is phantom reachable if it is neither strongly nor softly nor weakly reachable and has been finalized and there is a path from the roots to it that contains at least one phantom reference. The PhantomReference constructor accepts two arguments: referent - the object the new phantom reference will refer to q - the reference is registered with the given queue. The argument q represents the instance of the ReferenceQueue class. If the garbage collector determines that the referent of a phantom reference is phantom reachable, then the PhantomReference will be added to this ReferenceQueue. You can then retrieve the PhantomReference by using the remove() methods of the ReferenceQueue class. Consider the following example, ReferenceQueue q = new ReferenceQueue(); PhantomReference pr = new PhantomReference(object, referenceQueue); // Later on another point Reference r = q.remove(); // Now, clear up any thing you want PhantomReference, when to use? Phantom Reference can be used in situations, where sometime using finalize() is not sensible thing to do.This reference type differs from the other types defined in java.lang.ref Package because it isn't meant to be used to access the object, but as a signal that the object has already been finalized, and the garbage collector is ready to reclaim its memory. As per API doc, Phantom reference objects, which are enqueued after the collector determines that their referents may otherwise be reclaimed. Phantom references are most often used for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism. People usually attempt to use finalize() method to perform postmortem cleanup on objects which usually not advisable. As mentioned earlier, Finalizers have an impact on the performance of the garbage collector since Objects with finalizers are slow to garbage collect. Phantom references are safe way to know an object has been removed from memory. For instance, consider an application that deals with large images. Suppose that we want to load a big image in to memory when large image is already in memory which is ready for garbage collected. In such case, we want to wait until the old image is collected before loading a new one. Here, the phantom reference is flexible and safely option to choose. The reference of the old image will be enqueued in the ReferenceQueue once the old image object is finalized. After receiving that reference, we can load the new image in to memory. Similarly we can use Phantom References to implement a Connection Pool. We can easily gain control over the number of open connections, and can block until one becomes available. Reference Objects and Garbage Collection Soft Reference can be garbage collected after there are no strong references to the referent. However, it typically retained until memory is low. All softly reachable objects will be reclaimed before an OutOfMemoryException is thrown. Therefore, it can be used to implement caches of objects that can be recreated if needed. Weak Reference can be garbage collected when there are no strong or soft references to the referent. However, unlike Soft Reference, they are garbage collected on a gc even when memory is abundant. They often can be used for “canonical mappings” where each object has a unique identifier (one-to-one), and in collections of “listeners” On the other hand, Phantom Reference, can be garbage collected once there are no strong, soft or weak references to the referent. When object is phantomly reachable, it means the object is already finalized but not yet reclaimed, so the GC enqueues it in a ReferenceQueue for post-finalization processing. As per Java Doc, Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable. A PhantomReference is not automatically cleared when it is enqueued, so when we remove a PhantomReference from a ReferenceQueue, we must call its clear() method or allow the PhantomReference object itself to be garbage-collected. Summary In short, we should avoid finalize() as much as possible. There is no guarantee if the finalize() method will be called promptly following garbage collection, or even it will be called. If the finalize method runs for a long time, it can delay execution of finalize methods of other objects. Instead of relying on finalize(), we can use reference types define in java.lang.ref package. Beside java.lang.ref package, Google collection library also provide some alternatives. For example, FinalizablePhantomReference extends java.lang.ref.PhantomReference, deals with processing the ReferenceQueue and call back a convenient method finalizeReferent(). So if we want to do some cleanup operation when an object is claimed by the garbage collector (GC) then we just need to override the finalizeReferent() method. Resources Garbage Collection PhantomReference http://docstore.mik.ua/orelly/java-ent/jnut/ch13_01.htm Understanding Weak References - See more at: http://muhammadkhojaye.blogspot.com/2010/05/understanding-phantom-references.html
May 7, 2010
by Muhammad Ali Khojaye
· 65,723 Views · 9 Likes
article thumbnail
Add Comments and Javadocs in Eclipse With a Single Keystroke
When you want to work with comments in Eclipse, you could use the slow way of moving to the start of the line, pressing // and then repeating this for all the lines you have. Or you could use the quick way of adding a comment with a single keystroke no matter where the cursor’s positioned in the statement. The same goes for Javadocs – there are just too many things to type before you can start commenting the good stuff. That’s why Eclipse also has a shortcut that let’s you add Javadoc to a field, method or class. Keyboard shortcuts for comments and JavaDocs Here are the keyboard shortcuts for manipulating comments. Shortcut Command Description Ctrl+/ Toggle Comment Add/remove line comments (//…) from the current line. The position of the cursor can be anywhere on the line. Works with multiple selected lines as well. Ctrl+Shift+/ Add Block Comment Wrap the selected lines in a block comment (/*… */). Ctrl+Shift+\ Remove Block Comment Remove a block comment (/*… */) surrounding the selected lines. Alt+Shift+J Add Javadoc Comment Add a Javadoc comment to the active field/method/class. See the notes below for more details on where to position the cursor. Bear the following in mind when using Add Javadoc comment (Alt+Shift+J): To add a comment to a field, position the cursor on the field declaration. To add a comment to a method, position the cursor anywhere in the method or on its declaration. To add a comment to a class, the easiest is to position the cursor on the class declaration. Also works if you’re in the class, but not in a method, field or nested type. The Javadoc comment inserted is based on the Code Templates defined under Window > Preferences > Java > Code Style > Code Templates. If you expand the Comments section, you can change the default for Fields, Methods, Types (eg. classes), etc. Here’s a video to give you an idea of how fast and easy it is to add/remove comments using these shortcuts. The video shows toggling of single line comments, block comments and also adding a Javadoc comment to the method and class. Once I’ve commented out lines, I often find myself copying them and moving them around (eg. to try different variations of the code). You can do this faster by moving and copying lines using with a single keystroke. You can also have Eclipse format the comments whenever you save, saving you formatting time. From http://eclipseone.wordpress.com/2010/05/05/add-comments-and-javadocs-in-eclipse-with-a-single-keystroke/
May 6, 2010
by Byron M
· 178,198 Views · 2 Likes
article thumbnail
Practical PHP Patterns: Table Data Gateway
The Table Data Gateway pattern is the object-oriented equivalent of a relational table. In fact, this pattern's intent is to encapsulate the full interaction with a database table, holding all the logic specific to this particular implementation of the back end. In the majority of cases, a Table Data Gateway deals with a relational model, having a 1:1 relationship with the main tables of the database. Minor tables may not need a specific class, or can be managed via Table Data Gateways of tables that link them with foreign keys (for example entities introduced to store M:N relationships are usually not first-class citizens.) In the relational implementation, the Table Data Gateway handles all SQL queries, presenting a domain-specific interface when a class is coded for a specific table, or a generic one when a generic implementation is reused throughout different applications. The difference between the two APIs may be something like findBy($field, $value) (generic) versus findByPrice($price) (domain-specific). Note that in PHP magic methods are often used to implement domain-specific interfaces without code generation: a __call() implementation can catch the various findBy*() method and throw exceptions if the methods is not applicable. Related patterns Although, the concept of table is already correlated with a relational model (and it does not hold when the back end is an object-oriented database or one of the key-value stores so trendy today), this pattern is named Gateway because it is a specialization of the Gateway category of pattern, which decouple an object graph (or any in-memory structures) from external infrastructure like databases, web services, filesystems and so on. In fact, there is an alternate name for this pattern: Data Access Object (or DAO for friends). Although if I was pedantic I would highlight the differences between the implementations of DAOs and Table Data Gateway, their intent is really the same and there are differences in an individual pattern implementations that are greater than the ones between the different patterns. There's no clear demarcation line between the two. Another related pattern is the Table Module one. Table Data Gateway does not work against, but with a Table Module, providing a separation of concerns: the first object takes the rows out of the database, while the second performs in-memory operation on them (generally by composing the Table Data Gateway or its results). The in-memory operations of a Table Module are easier to test, but the SQL-based operations of the Table Data Gateway are pushed on the database side: there is a trade-off between the logic should be kept in each class. When used in isolation, the Table Data Gateway is also a Factory for also for Row Data Gateways or Active Records, both again implemented with generic or domain-specific interfaces. Many frameworks and first-generation PHP ORMs based on Active Record are also based on Table Data Gateway to provide a collection-level access to the objects stored as rows. In the context of Active Record, the only alternative to a Table Data Gateway to handle operations like find() is to place static methods on the Active Record class, with all the testability and dishonest API issues that ensue. Both Zend Framework and Doctrine 1.x represent tables as first-class objects. Examples Zend Framework's component Zend_Db, which is explored in the sample code, provides always generic implementations of Zend_Db_Table, and the possibility of optional subclassing (to add domain-specific methods). It is not recommend to expose the API of Table Data Gateway in front-end code, but it's a simple solution when the business logic does not warrant a full-featured Domain Model. Even when working with a Domain Model, and before the introduction of generic Data Mappers for PHP, the Table Data Gateway can be used in a composition solution (wrapped) to craft a simple API for a domain-specific Data Mapper, resulting in decoupling from the database. As I've written earlier, the sample code is taken from the Zend_Db_Table class of Zend Framework (actually from its parent abstract class, Zend_Db_Table_Abstract). I've enriched the docblock comments and left out all the methods not part of the main API (most of getters and setters for configuration and protecte|private members). $value) { switch ($key) { case self::ADAPTER: $this->_setAdapter($value); break; case self::DEFINITION: $this->setDefinition($value); break; case self::DEFINITION_CONFIG_NAME: $this->setDefinitionConfigName($value); break; case self::SCHEMA: $this->_schema = (string) $value; break; case self::NAME: $this->_name = (string) $value; break; case self::PRIMARY: $this->_primary = (array) $value; break; case self::ROW_CLASS: $this->setRowClass($value); break; case self::ROWSET_CLASS: $this->setRowsetClass($value); break; case self::REFERENCE_MAP: $this->setReferences($value); break; case self::DEPENDENT_TABLES: $this->setDependentTables($value); break; case self::METADATA_CACHE: $this->_setMetadataCache($value); break; case self::METADATA_CACHE_IN_CLASS: $this->setMetadataCacheInClass($value); break; case self::SEQUENCE: $this->_setSequence($value); break; default: // ignore unrecognized configuration directive break; } } return $this; } /** * Inserts a new row. * The data structure is as generic as possible. The list of columns is * known by configuration. * $this->_db is a light abstraction over PDO, which already encapsulates * most of the SQL. Database abstraction is not a banal task and segregating * the functionalities in different classes is very helpful. * * @param array $data Column-value pairs. * @return mixed The primary key of the row inserted. */ public function insert(array $data) { $this->_setupPrimaryKey(); /** * Zend_Db_Table assumes that if you have a compound primary key * and one of the columns in the key uses a sequence, * it's the _first_ column in the compound key. */ $primary = (array) $this->_primary; $pkIdentity = $primary[(int)$this->_identity]; /** * If this table uses a database sequence object and the data does not * specify a value, then get the next ID from the sequence and add it * to the row. We assume that only the first column in a compound * primary key takes a value from a sequence. */ if (is_string($this->_sequence) && !isset($data[$pkIdentity])) { $data[$pkIdentity] = $this->_db->nextSequenceId($this->_sequence); } /** * If the primary key can be generated automatically, and no value was * specified in the user-supplied data, then omit it from the tuple. */ if (array_key_exists($pkIdentity, $data) && $data[$pkIdentity] === null) { unset($data[$pkIdentity]); } /** * INSERT the new row. */ $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name; $this->_db->insert($tableSpec, $data); /** * Fetch the most recent ID generated by an auto-increment * or IDENTITY column, unless the user has specified a value, * overriding the auto-increment mechanism. */ if ($this->_sequence === true && !isset($data[$pkIdentity])) { $data[$pkIdentity] = $this->_db->lastInsertId(); } /** * Return the primary key value if the PK is a single column, * else return an associative array of the PK column/value pairs. */ $pkData = array_intersect_key($data, array_flip($primary)); if (count($primary) == 1) { reset($pkData); return current($pkData); } return $pkData; } /** * Updates existing rows. * Again we see generic data structures, not tied to PDO * or to particular adapters. * * @param array $data Column-value pairs. * @param array|string $where An SQL WHERE clause, or an array of SQL WHERE clauses. * @return int The number of rows updated. */ public function update(array $data, $where) { $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name; return $this->_db->update($tableSpec, $data, $where); } /** * Deletes existing rows. * * @param array|string $where SQL WHERE clause(s). * @return int The number of rows deleted. */ public function delete($where) { $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name; return $this->_db->delete($tableSpec, $where); } /** * Fetches rows by primary key. The argument specifies one or more primary * key value(s). To find multiple rows by primary key, the argument must * be an array. * * This method accepts a variable number of arguments. If the table has a * multi-column primary key, the number of arguments must be the same as * the number of columns in the primary key. To find multiple rows in a * table with a multi-column primary key, each argument must be an array * with the same number of elements. * * The find() method always returns a Rowset object, even if only one row * was found. * * @param mixed $key The value(s) of the primary keys. * @return Zend_Db_Table_Rowset_Abstract Row(s) matching the criteria. * @throws Zend_Db_Table_Exception */ public function find() { $this->_setupPrimaryKey(); $args = func_get_args(); $keyNames = array_values((array) $this->_primary); if (count($args) < count($keyNames)) { require_once 'Zend/Db/Table/Exception.php'; throw new Zend_Db_Table_Exception("Too few columns for the primary key"); } if (count($args) > count($keyNames)) { require_once 'Zend/Db/Table/Exception.php'; throw new Zend_Db_Table_Exception("Too many columns for the primary key"); } $whereList = array(); $numberTerms = 0; foreach ($args as $keyPosition => $keyValues) { $keyValuesCount = count($keyValues); // Coerce the values to an array. // Don't simply typecast to array, because the values // might be Zend_Db_Expr objects. if (!is_array($keyValues)) { $keyValues = array($keyValues); } if ($numberTerms == 0) { $numberTerms = $keyValuesCount; } else if ($keyValuesCount != $numberTerms) { require_once 'Zend/Db/Table/Exception.php'; throw new Zend_Db_Table_Exception("Missing value(s) for the primary key"); } $keyValues = array_values($keyValues); for ($i = 0; $i < $keyValuesCount; ++$i) { if (!isset($whereList[$i])) { $whereList[$i] = array(); } $whereList[$i][$keyPosition] = $keyValues[$i]; } } $whereClause = null; if (count($whereList)) { $whereOrTerms = array(); $tableName = $this->_db->quoteTableAs($this->_name, null, true); foreach ($whereList as $keyValueSets) { $whereAndTerms = array(); foreach ($keyValueSets as $keyPosition => $keyValue) { $type = $this->_metadata[$keyNames[$keyPosition]]['DATA_TYPE']; $columnName = $this->_db->quoteIdentifier($keyNames[$keyPosition], true); $whereAndTerms[] = $this->_db->quoteInto( $tableName . '.' . $columnName . ' = ?', $keyValue, $type); } $whereOrTerms[] = '(' . implode(' AND ', $whereAndTerms) . ')'; } $whereClause = '(' . implode(' OR ', $whereOrTerms) . ')'; } // issue ZF-5775 (empty where clause should return empty rowset) if ($whereClause == null) { $rowsetClass = $this->getRowsetClass(); if (!class_exists($rowsetClass)) { require_once 'Zend/Loader.php'; Zend_Loader::loadClass($rowsetClass); } return new $rowsetClass(array('table' => $this, 'rowClass' => $this->getRowClass(), 'stored' => true)); } return $this->fetchAll($whereClause); } /** * Fetches a new blank row (not from the database). * Thanks to the metadata, a new Row Data Gateway can be created. This * if a Factory Method. The dynamic nature of PHP makes configuring the * subclass for the Row Data Gateway as simple as defining a string. * * @param array $data OPTIONAL data to populate in the new row. * @param string $defaultSource OPTIONAL flag to force default values into new row * @return Zend_Db_Table_Row_Abstract */ public function createRow(array $data = array(), $defaultSource = null) { $cols = $this->_getCols(); $defaults = array_combine($cols, array_fill(0, count($cols), null)); // nothing provided at call-time, take the class value if ($defaultSource == null) { $defaultSource = $this->_defaultSource; } if (!in_array($defaultSource, array(self::DEFAULT_CLASS, self::DEFAULT_DB, self::DEFAULT_NONE))) { $defaultSource = self::DEFAULT_NONE; } if ($defaultSource == self::DEFAULT_DB) { foreach ($this->_metadata as $metadataName => $metadata) { if (($metadata['DEFAULT'] != null) && ($metadata['NULLABLE'] !== true || ($metadata['NULLABLE'] === true && isset($this->_defaultValues[$metadataName]) && $this->_defaultValues[$metadataName] === true)) && (!(isset($this->_defaultValues[$metadataName]) && $this->_defaultValues[$metadataName] === false))) { $defaults[$metadataName] = $metadata['DEFAULT']; } } } elseif ($defaultSource == self::DEFAULT_CLASS && $this->_defaultValues) { foreach ($this->_defaultValues as $defaultName => $defaultValue) { if (array_key_exists($defaultName, $defaults)) { $defaults[$defaultName] = $defaultValue; } } } $config = array( 'table' => $this, 'data' => $defaults, 'readOnly' => false, 'stored' => false ); $rowClass = $this->getRowClass(); if (!class_exists($rowClass)) { require_once 'Zend/Loader.php'; Zend_Loader::loadClass($rowClass); } $row = new $rowClass($config); $row->setFromArray($data); return $row; } }
May 5, 2010
by Giorgio Sironi
· 8,004 Views
article thumbnail
Practical PHP Patterns: Service Layer
The last domain logic pattern we will treat in this series is the Service Layer one. In its simplest form, a Service Layer is a set of service classes that deal with application logic, and that are characterized from being used from different front-ends. Source code, at every level of abstraction, is the representation of data entities and their related behavior, particularly in an object-oriented paradigm. There are different types of logic which this behavior can be partitioned into: business logic is encapsulated in a Domain Model, and it is specific to the particular domain the application works in. The added value of an enterprise application ensues primarily from its business logic. application logic is in the scope of a Service Layer, and it is not strictly domain-specific, although its implementation may be. For example, translating objects into an XML or Json representation is part of application logic, even if it is executed with application-specific classes which depends on an underlying Domain Model. The task of representing data in a particular format is oblivious to the domain, as it does not belong to forums or social networks platforms only, or to an electronic or chemistry domain. presentation logic finds in an user interface its quintessence, and it can be considered as the subset of application logic which governs the end user view of the system. I would not consider a difference of format in the whole representation of an object as presentation logic, though, as it falls into the realm of reusability I would want to keep in a Service Layer. Commonly this different concerns of an application are organized in different layers, where each layer resides on the top of the previous one. In classic approaches, there is an infrastructure layer which the business logic layer depends on, and which deals for example with persistence issues. In more evolved approaches, however, keeping the Domain Model as the very core of the application is the most sound choice, moving infrastructure in a Service Layer which can plug in the Domain Model via implementing certain adapter interfaces (like a Repository or a Factory). Thus a Service Layer is particularly useful for example when there are different front-ends to a common Domain Model. These front-ends, such as user interfaces or REST Apis, delegate the application logic to a Service Layer, which encapsulates it. Pushing this logic into the Domain Model would clutter the core of the application, since is is not strictly necessary to work with it. Responsibilities of a Service Layer include, for starters, CRUD functions over objects of the respective Domain Model. Some of these responsibilities may be already included in the Domain Model (Factories for Entities and Value Objects), while other ones are usually only specified as interfaces with the implementations left to an infrastructure Service Layer (Repositories). Example of the latter components are the bread and butter of a Service Layer. Persistence-related actions such as saving objects in a database, data translation (to and from Json, XML, yaml), integration of mail and every service which do not reside in the same PHP execution environment of the Domain Model is a candidate for a class or component in a Service Layer, whose implementations can be stubbed out in acceptance testing. Note that services are also implemented in a Domain Model when they do not contain knowledge which spans outside of the domain and of basic language structures. Generally speaking, services are a point of connection between different Entities and objects, which accomodate operations that would couple the other objects together if implemented directly on them. Only when there are more concerns involved than the execution of logic on domain objects - persistence or orthogonal operations - these services shall be moved to an upper level component like a Service Layer. In some cases, the Service Layer becomes overly generic and orthogonal to the underlying Domain Model, to the point that it is recycled in different applications. Object-relational mappers are part of the infrastructure, and a common example of reusable services. Though, the composition of libraries objects (has-a relationship) is preferable to the direct usage of them, or to their subclassing. Generic frameworks and libraries have a catch-all Api, while a specific Service Layer defines only the use cases actually employed by the front-ends - for example removing the unnecessary update of crucial entities that should be immutable by modelling. Finally, a Service Layer can work without an underlying Domain Model, by interfacing to external web (and non-web) services. If there is local state involved, however, it is difficult to avoid having a basic Domain Model just to define data structures that the Services pass around. The client code, which resides in front-end, has no knowledge of the difference between methods that act over a local Domain Model or an external entity: the Service Layer effectively isolates the upper layer from changes in the lower one. The code sample builds on the sample presented in the Domain Model article, continuing with the idea of an upper level layer. The sample features two service classes, one as a stubbed implementation of an interface of the Domain Model and one that it is employed only at an higher level. setSender('[email protected]'); $mail->setRecipient('[email protected]'); $mail->setSubject('Important stuff'); $mail->setText('I wanted to talk you about...'); return array( $mail ); } } /** * Service that belongs only to the very Service Layer. * It may have an interface, but it will be kept in this layer. */ class EmailTransferService { /** * Transforming an object to XML is a common task for a Service Layer. * However, dependencies towards the Domain Model are well-accepted * but they don't mean that this layer's classes should be * included in the lower layer. * Keeping the XML transformation in one place aids different front-ends * in sharing code. */ public function toXml(Email $mail) { return "\n" . " " . $mail->getSender() . "\n" . " " . $mail->getRecipient() . "\n" . " " . $this->_encode($mail->getSubject()) . "\n" . " " . $this->_encode($mail->getText()) . "\n" . "\n"; } protected function _encode($text) { return htmlentities($text); } } // client code $repository = new DumbEmailRepository(); $transferService = new EmailTransferService(); foreach ($repository->getEmailsFor('[email protected]') as $mail) { echo $transferService->toXml($mail); }
May 2, 2010
by Giorgio Sironi
· 24,501 Views
  • Previous
  • ...
  • 876
  • 877
  • 878
  • 879
  • 880
  • 881
  • 882
  • 883
  • 884
  • 885
  • ...
  • Next
  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook
×