001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.cdi;
018
019import javax.enterprise.context.Conversation;
020import javax.inject.Inject;
021
022import org.apache.wicket.Component;
023import org.apache.wicket.Page;
024import org.apache.wicket.application.IComponentOnBeforeRenderListener;
025import org.apache.wicket.request.IRequestHandler;
026import org.apache.wicket.request.cycle.RequestCycle;
027import org.apache.wicket.util.visit.IVisit;
028import org.apache.wicket.util.visit.IVisitor;
029import org.apache.wicket.util.visit.Visits;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * Automatically starts and ends conversations for pages with a
035 * {@link ConversationalComponent}.
036 * 
037 * @author papegaaij
038 */
039public class AutoConversationManager implements IComponentOnBeforeRenderListener
040{
041        private static final Logger logger = LoggerFactory.getLogger(AutoConversationManager.class);
042
043        @Inject
044        private Conversation conversation;
045
046        @Inject
047        private AutoConversation autoConversation;
048
049        private IConversationPropagation propagation;
050
051        public AutoConversationManager(IConversationPropagation propagation)
052        {
053                NonContextual.of(AutoConversationManager.class).inject(this);
054                this.propagation = propagation;
055        }
056
057        @Override
058        public void onBeforeRender(Component component)
059        {
060                if (component instanceof Page)
061                {
062                        Page page = (Page)component;
063                        IRequestHandler activeRequestHandler = page.getRequestCycle().getActiveRequestHandler();
064                        autoEndIfNecessary(page, activeRequestHandler);
065                        autoBeginIfNecessary(page, activeRequestHandler);
066                }
067        }
068
069        protected void autoBeginIfNecessary(Page page, IRequestHandler handler)
070        {
071                if (conversation == null || !conversation.isTransient() || page == null
072                                || !hasConversationalComponent(page) || !propagation.propagatesVia(handler, page))
073                {
074                        return;
075                }
076
077                // auto activate conversation
078
079                conversation.begin();
080                autoConversation.setAutomatic(true);
081
082                logger.debug("Auto-began conversation '{}' for page '{}'", conversation.getId(), page);
083        }
084
085        protected void autoEndIfNecessary(Page page, IRequestHandler handler)
086        {
087                if (conversation == null || conversation.isTransient() || page == null
088                                || hasConversationalComponent(page) || !propagation.propagatesVia(handler, page)
089                                || autoConversation.isAutomatic() == false)
090                {
091                        return;
092                }
093
094                // auto de-activate conversation
095
096                String cid = conversation.getId();
097
098                autoConversation.setAutomatic(false);
099                conversation.end();
100                ConversationPropagator.removeConversationIdFromPage(page);
101
102                logger.debug("Auto-ended conversation '{}' for page '{}'", cid, page);
103        }
104
105        protected boolean hasConversationalComponent(Page page)
106        {
107                Boolean hasConversational = Visits.visit(page, new IVisitor<Component, Boolean>()
108                {
109                        @Override
110                        public void component(Component object, IVisit<Boolean> visit)
111                        {
112                                if (object instanceof ConversationalComponent)
113                                {
114                                        visit.stop(true);
115                                }
116                        }
117                });
118
119                return hasConversational == null ? false : hasConversational;
120        }
121}