001    package crisp.wbwiki.server;
002    
003    import javax.servlet.ServletException;
004    
005    import org.springframework.context.ApplicationContext;
006    import org.springframework.context.support.FileSystemXmlApplicationContext;
007    
008    import com.google.gwt.user.server.rpc.RemoteServiceServlet;
009    
010    import crisp.wbwiki.client.Page;
011    import crisp.wbwiki.client.WikiService;
012    
013    
014    /**
015     * Servlet that receives http requests from Whiteboard Wiki via AJAX.
016     * GWT handles the HTTP requests (in superclass RemoteServiceServlet) 
017     * and translates them into simple RPC method calls on WikiServiceImpl. <p>
018     * 
019     * Persistence logic is delegated to a WikiDb. This servlet requires a spring application context, 
020     * which it uses to look up the WikiDb instance. 
021     * Usually this means you should have a /WEB-INF/applicationContext.xml.
022     * For details see:
023     * <ul>
024     *   <li>http://www.springframework.org/docs/api/org/springframework/web/context/support/WebApplicationContextUtils.html</li>
025     *   <li>http://www.springframework.org/docs/api/org/springframework/web/context/ContextLoader.html</li>    
026     * </ul>
027     * 
028     * @author Henrik Kniberg
029     */
030    public class WikiServiceImpl extends RemoteServiceServlet implements WikiService {
031            private static final long serialVersionUID = 1L;
032            
033            private static final String SPRING_CONTEXT_PATH = "classpath:/wbWikiContext.xml";
034            private WikiDb wikiDb;
035                    
036            
037            public WikiServiceImpl() {
038                    super();
039            }
040            
041            public WikiServiceImpl(WikiDb wikiDb) {
042                    super();
043                    setWikiDb(wikiDb);
044            }
045    
046            @Override
047            public void init() throws ServletException {
048                    super.init();
049                    
050                    //Load spring context
051                    ApplicationContext springContext;
052                    springContext = new FileSystemXmlApplicationContext(SPRING_CONTEXT_PATH);
053                    
054                    //Get wikiDb bean from context
055                    setWikiDb((WikiDb) springContext.getBean("wikiDb", WikiDb.class));
056            }
057    
058            protected WikiDb getWikiDb() {
059                    return wikiDb;
060            }
061    
062            protected void setWikiDb(WikiDb wikiDb) {
063                    this.wikiDb = wikiDb;
064            }
065    
066            public Page synchronize(Integer lastVersionLoadedByClient, String modifiedContentInClient) {
067                    if (modifiedContentInClient != null) {
068                            //Aha, new content from the client!
069                            if (lastVersionLoadedByClient == null) {
070                                    throw new IllegalArgumentException("lastVersionLoadedByClient may only be null if modifiedContentInClient is null!");
071                            }
072                            
073                            int updatedVersion = wikiDb.savePage(modifiedContentInClient);
074                            if (updatedVersion == lastVersionLoadedByClient + 1) {
075                                    //Ok, no concurrent edits have occurred. 
076                                    //No need to return anything to the client.
077                                    return null;
078                            } else {
079                                    //Oops, concurrent edits have occurred.
080                                    //Send the client an updated state.
081                                    return new Page(updatedVersion, modifiedContentInClient);
082                            }                       
083                    } else {
084                            //OK, no new content from the client.
085                            //Check if there is new content in the DB.
086                            synchronized(wikiDb) {
087                                    int currentVersion = wikiDb.getCurrentVersion();
088                                    if (lastVersionLoadedByClient == null || currentVersion != lastVersionLoadedByClient) {
089                                            //Yup, there is new content.
090                                            //Send the client an updated page.
091                                            return wikiDb.loadPage();
092                                    } else {
093                                            //Nope, no new content.
094                                            //No need to send anything back to the client.
095                                            return null;
096                                    }
097                            }
098                    }
099            }
100    
101    }