Apache Chemistry OpenCMIS provides open source implementations of the CMIS standard i.e. the abstraction layer for controlling different document management systems and ECM repositories using web protocols. OpenCMIS hides the binding details and provides APIs on different abstraction levels. Using the OpenCMIS client APIs, for example, you can develop high customizable front-end web applications that interact with the Alfresco Content Repository in a decoupled manner.
In a previous article I showed a simple example about the creation of a CMIS session using the OpenCMIS client APIs. Now, I want to move on to a much more advanced topic. In this post I show how to open and check a CMIS Session in a Spring MVC web application. The back-end ECM Content Repository will be Alfresco. To check a valid CMIS Session I use Spring HandlerInterceptor, the base object of the interceptor mechanism in the Spring MVC chain-execution (you can see more details in this article). Here is an high level representation of the client-server architecture.
In the above image the user makes access to the sampleace front-end webapp. If credentials are successfully passed, then a valid CMIS Session is created. The sampleaci is plugged-into a content repository to handle all content services using the CMIS specification and the user can interact with the Alfresco Content Repository through the sampleaci webapp.
IMPORTANT NOTICE about the openCMIS Session
“In order to be effective, this Session object has to be reused as much as possible! Don’t throw it away. Keep it and reuse it! OpenCMIS is thread-safe. The Session object can and should be reused across thread boundaries.”
Offcial wiki says OpenCMIS is thread-safe, it means the Session should be reused as much as possible. To do this I will use ThreadLocal object. Below you can see the application stack and the roadmap I used in this post.
APPLICATION STACK
– SO CentOS 6.5 x86_64
– Alfresco Community 4.2.f
– JDK 1.7.0_07
– Apache Maven 3.0.4
– Eclipse Helios
ROADMAP
STEP 1. Web project from-scratch using Maven
STEP 2. Spring MVC Dispatcher
STEP 3. Session CMIS and ThreadLocal
STEP 4. Session CMIS Interceptor
STEP 5. Spring Controller
STEP 6. Logging and test
STEP 1. Web project from-scratch using Maven
Create a web project skeleton using Maven and import the project in Eclipse.
$ mvn archetype:generate -DgroupId=eu.giuseppeurso.sampleaci -DartifactId=sample-alfresco-cmis-interceptor -DarchetypeArtifactId=maven-archetype-webapp $ mvn eclipse:eclipse
STEP 2. Spring MVC Dispatcher
Spring MVC dependencies in pom.xml and Spring DispatcherServlet definition in web.xml file.
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${org.springframework-version}</version> <exclusions> <!-- Exclude Commons Logging in favor of SLF4j --> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${org.springframework-version}</version> </dependency>
<servlet> <servlet-name>spring-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/spring-dispatcher.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
STEP 3. Session CMIS e ThreadLocal
OpenCMIS dependencies in pom.xml file.
<dependency> <groupId>org.apache.chemistry.opencmis</groupId> <artifactId>chemistry-opencmis-commons-api</artifactId> <version>0.10.0</version> </dependency> <dependency> <groupId>org.apache.chemistry.opencmis</groupId> <artifactId>chemistry-opencmis-client-api</artifactId> <version>0.10.0</version> </dependency> <dependency> <groupId>org.apache.chemistry.opencmis</groupId> <artifactId>chemistry-opencmis-client-impl</artifactId> <version>0.10.0</version> </dependency>
Create a class for the CMIS Session management. To create a CMIS Session against Alfresco, the createSession method makes use of the ATOMPUB service url. To reuse a valid CMIS Session, the currentSession saves the created session in a local thread.
// A ThreadLocal to save a valid CMIS session private static ThreadLocal currentSession = new ThreadLocal() { protected synchronized Object initialValue() { return null; } };
// The method to create the CMIS Session public void createSession(String username, String passwd, String url) throws Exception { Session session = null; SessionFactory sessionFactory = SessionFactoryImpl.newInstance(); //Something here..... // Create session. try { Repository ecmRepository = sessionFactory.getRepositories(parameter).get(0); session = ecmRepository.createSession(); session.getDefaultContext().setCacheEnabled(true); } catch (CmisConnectionException e) { throw new Exception(e); } // Save session in local thread currentSession.set(session); }
Here is a configuration file used by a ResouceBundle to externalize the Alfresco CMIS service url. Pay attention to the two different urls for Alfresco vers 4.0 and vers 4.2 (uncommented).
### Alfresco base url alfresco.url=http://localhost:8080 ### CMIS Endpoint for Alfresco 4.0 #alfresco.cmis.binding=/alfresco/cmisatom ### CMIS Endpoint for Alfresco 4.2 alfresco.cmis.binding=/alfresco/api/-default-/public/cmis/versions/1.1/atom
STEP 4. Session CMIS Interceptor
The Spring HandlerInterceptor takes care of intercepting all the requests on the webapp. This HandlerInterceptor‘s implementation, always checks a valid CMIS Session in the preHandle method (more info in this article).
@Override public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception { String username = request.getParameter("username"); String password = request.getParameter("password"); // Intercepting the requests when a CmisSession is valid. if (SessionCmis.getCurrentSession() != null) { if (request.getRequestURI().equals("/sampleaci/")) { log.debug("Redirect to the home page."); response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/home")); } } //Intercepting the requests except the login and login-failed, when a CimisSession is not valid. else { if( (!request.getRequestURI().equals("/sampleaci/")) && (!request.getRequestURI().equals("/sampleaci/login-failed")) && (username==null && password==null) ){ response.sendRedirect(response.encodeRedirectURL(request.getContextPath())); } } return true; }
The Interceptor definition in the spring-dispatcher.xml file.
<interceptors> <interceptor> <mapping path="/*"/> <beans:bean class="eu.giuseppeurso.sampleaci.cmis.SessionCmisInterceptor" /> </interceptor> </interceptors>
STEP 5. Spring Controller
Here is a resume of the mapping requests for the Controller of the login and welcome pages (source code here).
# LoginController Mapped "{[/login],methods=[POST] --> LoginController.login Mapped "{[/logout],methods=[GET] --> LoginController.logout Mapped "{[/],methods=[GET] --> LoginController.showLogin Mapped "{[/login-failed],methods=[GET] --> LoginController.loginFailed # HomeController Mapped "{[/home],methods=[GET] --> HomeController.home
The LoginController implements the method to process the login credentials submitted by the user.
/** * The POST method to submit login credentials. * @throws Exception */ @RequestMapping(value = "/login", method = RequestMethod.POST) public String login(Model model, LoginForm loginForm, Locale locale) throws Exception { String username = loginForm.getUsername(); String password = loginForm.getPassword(); String repourl = settings.getString("alfresco.url") + settings.getString("alfresco.cmis.binding"); try { SessionCmis session = new SessionCmis(); session.createSession(username, password, repourl); } catch (Exception e) { logger.info("Failed to created CMIS Session"); e.printStackTrace(); } if (SessionCmis.getCurrentSession()!=null) { logger.info("Successfully logged in for username '"+ username+"' at: "+formattedDate); return "home"; } else { logger.info("Login failed for username '"+ username+"' at: "+formattedDate); return "redirect:/login-failed"; } }
STEP 6. Logging and test
Some logging definition in the log4j.xml file. Note the package eu.giuseppeurso.sampleaci.cmis is setted to debug.
<!-- Application Loggers --> <logger name="eu.giuseppeurso.sampleaci.cmis"> <level value="debug" /> </logger> <logger name="eu.giuseppeurso.sampleaci.web.controller"> <level value="info" /> </logger>
Start a instance of Alfresco, deploy the sampleaci webapp in the Eclipse Tomcat and run some tests of access. Here is some results.