I'm currently in the process of developing a simple IMAP Email client as part of a larger Spring MVC web application. The mail-related stuff is handled by the Java Mail API.
I am neither a Java pro nor am I very experienced with the Spring framework, so please bare with me. I'm having this 'project' as a hobby and try to learn from and grow with it.
I was already able to send mail and read IMAP messages from a server, so this is not the real issue here. I'm having a hard time to figure out how to properly and cleanly integrate the mail functionality into the mvc environment.
The goal front-end-wise, is to enable the user to get a list with messages and then be able to set certain flags (read, deleted, etc.) on them.
Now there are some conceptional things that I can't really figure out. I'll just describe how I imagine this to work and what problems I have with it. Maybe I'm just overthinking this...
I imagine a Controller with a request mapping to /mail. The method should return the view and add the messages to the model. To have a reference point on the messages I think about using a custom Wrapper object around each javax.mail.Message object. These wrapper objects should then have an ID, that I could pass back and forth between back- and front-end since the Message itself doesn't have any identifying property except for the mail-header (?).
When the user deletes or marks a message with some other flag, an AJAX post request to a second request mapping in the controller should be called, passing the wrapper object id that I use to get the real message from some list.
This is where I get stuck... How would I properly store a collection of those message-wrapper objects so that I can access them from different mapping methods in the controller?
Populate a list in the controller constructor? What's the lifespan of a controller instance?
I'm also unsure about the IMAP connection here. To get the messages I have to open the store and the folder. Once the store is closed, the connection between the received Message objects and the server is lost so I can't modify them anymore...
Can I open the store on application startup and leave it open until shutdown? How would I do that?
Then again, if I opened the store and got the messages each time in the '/mail' request mapping method, I guess my IMAP server wouldn't like the amount of new connections so much, - so the connection should in fact be handled somewhere centralized and in the background, instead of inside the controller?
That's an insane amount of questions there I'd be really glad if someone could maybe just point me in the right direction with this.
If anything is unclear, please let me know and I'll try to clarify.
I guess I am not sure what it is exactly your trying to do. The answer to the question about the list in the controller is No! You should never create a stateful Spring bean and a @Controller is a spring bean (there is only one of the per Application Context).
It sounds like you are using an imap server, like Gmail? Anyway you can template the messages with Thymeleaf which integrates with Spring MVC quite nicely. I think they even have an example mail application here:
If you goal is to receive messages have a look at Spring Integration which has support for both sending and receiving mail.
Yes, you might be shootng the moon here. To make a construct in analogy, Spring is like a big digger. It helps you do a huge task quickly. The problem is if you are not comfortable digging with a shovel, you are going to hurt yourself trying to drive a big digger.. You need to have a feel for the underlying technology, and techniques and pitfalls to be effective. A lot of people who get hurt by spring are the ones that go straight to spring. I wouldn't recommend spring to anyone who us not an intermediate java developer.
To answer few of your questions. So basically you have one screen where user sees a list of items, clicks a link that takes him to a page that does something with the item. What you need to do is pass the item id as a parameter to the controller that is going to perform that action. The simplest way to do this is by posting a form. The id of the item should be a form parameter
Second, question regarding IMAP connections. The short answer is it depends, on whether you have connection pooling
Generally you don't want to do what you are thinking because of 2 reasons
A) connections take resources on the server that you are connected to. Keepin connection open while you are not using it is bad
B) you cannot use connections for concurrent threads
I'll illustrate with an example. Let's say you are transported to 1950s and you have a firm that has 20 managers. These managers don't know how to type, but they need to send letters. So, you hire a secretary to type letters. Managers dictate. Secretary types. Now, imagine one of the managers calls the secretary. She goes up there and starts typing. Now manager # 2 wants a letter types at the same time. What do you do? Do you have the secretary type both letters at the same time? No! She would go crazy right. She doesn't have the ability to keep 2 conversations in her head at the same time. A connection is like a secretary. It can only handle one conversation at a time.. If you have 2 users coming to your web app you cannot use the same connection for both users
So, now do you solve this problem. One thing you could do is hire a secretary when you need me. Fire her right after she finishes typing he letter. It's not crazy as it sounds. Yes there is going to be overhead, because you spend time hiring and firing secretaries. You spend 4 hours interviewing a secretary for a 20 minute job. Crazy, no? Not so crazy for a web app that doesn't have too much load. There is a cost to getting a connection, but not as bad as hiring a secretary. Enterprise apps won't do this because they are going to handle lot of load and those overheads add up. However since you are doing the to learn, this is what you should do. It's simpler.
Another thing you can do is hire a dedicated secretary for each manager. But if the manager spends only 20% of his time dictating letters then the secretary will be idle 80% of the time. The same thing with your app. You could have a dedicated connection for every user who can ever come to your app. An idle connection doesn't take as many resources as a live one, but it does take resources, and if you have a million users, all those resources add up
So, you cannot fire-hire secretaries when you need them, nor you can hire lot of secretaries. So, what can you do? The answer is have a "pool" of secretaries... And this is what businesses would do back when secretary meant being the typist. They would have a typing pool where all the secretaries would sit. When a manager required a secretary, he borrowed one from the pool, got her to type the letter,and then returned her back. Of course you have to decide the size of the pool. Hire too many secretaries and at least one of them will be always idle. Hire to less, and piss off some managers. So, you hire another manager whose job is to hire and fire secretaries. His job is to understand how many secretaries the business needs and she manages the hiring and firing. Which means that maybe a secretary would get hired and fired every few months, which is fine. You are minimizing overall cost.
So, what you really need is a connectin pool and this is what all industrial grade apps do. Connection pool comes with a manager that manages connections. You just need one instance of connection pool that you create n your spring XML and inject into your controller. The pool will create and destroy connections for you. The controller is like the manager of the 1950s company. It will simply borrow connection, use it, return it back.
In a nutshell. If you are using a non pooled connection, open and close it on every request. If you have a pooled connection, hold only one instance.
Sorry for the long answer but without knowing what kind of connection you are using, it's hard to say what should be done.
Wow, Jayesh, thanks for this detailed explanation! I really appreciate it. Though I think this kind of confused me some more.
First of all, I have no idea where to start to implement such a connection pool, but let's just ignore that for now.
What bothers me more right now is the Java Mail Message connection. Let's try an example, will contain pseudocode..
In the JSP we have a list/table with the messages and have hidden the appropriate id's somewhere. Now the user interacts over a jquery ajax call.
This request is handled by this additional method in my MailController above:
I hope this illustrates a little more what I'm trying to do. But then again, I wouldn't even know how to implement the connection pooling in the first place, so...
I've looked at the mail-related stuff in the spring-integration library that Bill gave a link to. Thanks btw! I think I'll read more on that as it looks like it might help - I'll know for sure when I've tried it
Oh and yes, I really forgot to mention it. I'm connecting to a private server running Postfix as MTA and Dovecot to handle IMAP.
You save more money with a clothesline than dozens of light bulb purchases. Tiny ad: