Project

General

Profile

Download (10.5 KB) Statistics
| Branch: | Tag: | Revision:
1
package eu.etaxonomy.vaadin.mvp;
2

    
3
import java.io.Serializable;
4

    
5
import org.apache.log4j.Logger;
6
import org.hibernate.Session;
7
import org.hibernate.engine.spi.SessionImplementor;
8
import org.springframework.beans.factory.annotation.Autowired;
9
import org.springframework.beans.factory.annotation.Qualifier;
10
import org.springframework.security.core.context.SecurityContext;
11
import org.springframework.security.core.context.SecurityContextHolder;
12
import org.springframework.transaction.IllegalTransactionStateException;
13

    
14
import com.vaadin.server.ServletPortletHelper;
15
import com.vaadin.server.VaadinRequest;
16
import com.vaadin.server.VaadinService;
17
import com.vaadin.server.VaadinSession;
18
import com.vaadin.ui.UI;
19

    
20
import eu.etaxonomy.cdm.api.application.CdmRepository;
21
import eu.etaxonomy.cdm.vaadin.server.CdmSpringVaadinServletService;
22
import eu.etaxonomy.cdm.vaadin.server.RequestStartListener;
23
import eu.etaxonomy.cdm.vaadin.session.IntraViewConversationDirector;
24
import eu.etaxonomy.cdm.vaadin.session.ViewScopeConversationHolder;
25
import eu.etaxonomy.vaadin.ui.navigation.NavigationManager;
26
import eu.etaxonomy.vaadin.ui.navigation.NavigationManagerBean;
27

    
28
/**
29
 * AbstractPresenter is the base class of all presenter components. Presenter's
30
 * role is to govern the view and control the complex UI logic based on
31
 * notifications presenter receives from its view.
32
 *
33
 * @author Peter / Vaadin
34
 *
35
 * @param <V>
36
 *            type of the view this presenter governs
37
 */
38
public abstract class AbstractPresenter<V extends ApplicationView> implements Serializable, IntraViewConversationDirector, RequestStartListener {
39

    
40

    
41
    private static final long serialVersionUID = 5260910510283481832L;
42

    
43
    public static final Logger logger = Logger.getLogger(AbstractPresenter.class);
44

    
45
	private V view;
46

    
47

    
48
	protected V getView() {
49
	    if(view == null){
50
            Logger.getLogger(this.getClass()).warn("CDM-VAADIN#6562: presenter " + toString() + " without view.");
51
        }
52
		return view;
53
	}
54

    
55
	@Autowired
56
	@Qualifier("cdmRepository")
57
	private CdmRepository repo;
58

    
59
	@Autowired
60
	private NavigationManager navigationManager;
61

    
62
	@Autowired
63
	private ViewScopeConversationHolder conversationHolder;
64

    
65
    protected boolean conversationBound;
66

    
67

    
68
	/**
69
	 * @return the repo
70
	 */
71
	public CdmRepository getRepo() {
72
	    if(!conversationBound){
73
	        // this is the central access point for getting access to the service layer.
74
	        // In case the presenter needs access to the repository, it most probably will use
75
	        // a service, so it is a good idea to bind the conversation at this point.
76
	        bindConversation();
77
	    }
78
	    return repo;
79
	}
80

    
81
	/**
82
     * @return
83
     *
84
     * FIXME is it ok to use the SecurityContextHolder or do we need to hold the context in the vaadin session?
85
     */
86
    protected SecurityContext currentSecurityContext() {
87
        return SecurityContextHolder.getContext();
88
    }
89

    
90
    /**
91
     * @return
92
     */
93
    protected Session getSession() {
94
        Session session = conversationHolder.getSession();
95
        logger.trace(this._toString() + ".getSession() - session:" + session.hashCode() +", persistenceContext: " + ((SessionImplementor)session).getPersistenceContext() + " - " + session.toString());
96
        return session;
97
    }
98

    
99
    protected String _toString(){
100
        return this.getClass().getSimpleName() + "@" + this.hashCode();
101
    }
102

    
103
	/**
104
	 * Notifies the presenter that its view is initialized so that presenter can
105
	 * start its own initialization if required.
106
	 *
107
	 * @param view
108
	 */
109
	protected final void init(V view) {
110
	    logger.trace(String.format("Presenter %s init()", _toString()));
111
		this.view = view;
112
		// bind the conversation to the thread of the first request send to the according View
113
		// all other requests are handled in onRequestStart()
114
		// logger.trace(String.format(">>>>> %s init() bind()", _toString()));
115
	    ensureBoundConversation();
116
	    // register as request start and end listener
117
	    VaadinService service = UI.getCurrent().getSession().getService();
118
	    if(service instanceof CdmSpringVaadinServletService){
119
	        logger.trace(String.format("~~~~~ %s register as request listener", _toString()));
120
	        ((CdmSpringVaadinServletService)service).addRequestEndListener(this);
121
	        if(logger.isTraceEnabled()){
122
	            ((CdmSpringVaadinServletService)service).addRequestStartListener(this);
123
	        }
124
	    } else {
125
	        throw new RuntimeException("Using the CdmSpringVaadinServletService is required for proper per view conversation handling");
126
	    }
127
		onPresenterReady();
128
	}
129

    
130
	/**
131
	 * Returns <code>true</code> for:
132
	 * <ul>
133
	 *   <li>..</li>
134
	 * <ul>
135
	 *
136
	 * Return <code>false</code> for:
137
	 *
138
	 * <ul>
139
     *   <li>UILD request in a existing view, like clicking on a button</li>
140
     * <ul>
141
     *
142
	 * @return
143
	protected boolean isActiveView(){
144
        return UI.getCurrent() != null && getView() != null && getView() == navigationManager.getCurrentView();
145
    }
146
	 */
147

    
148
    /**
149
     *
150
     */
151
	protected void bindConversation() {
152
        logger.trace(String.format(">>>>> %s bind()", _toString()));
153
        conversationHolder.bind();
154
        conversationBound = true;
155
    }
156

    
157
    @Override
158
    public void ensureBoundConversation() {
159
        if(!conversationBound){
160
            bindConversation();
161
        }
162
        if(!conversationHolder.isTransactionActive()){
163
            logger.trace(String.format(">>   %s starting transaction ", _toString()));
164
            conversationHolder.startTransaction();
165
        }
166
    }
167

    
168
    /**
169
     *
170
     */
171
    protected void unbindConversation() {
172
        logger.trace(String.format("<<<<< %s unbind()", _toString()));
173
        conversationHolder.unbind();
174
        // FIXME conversationHolder.isTransactionActive() always returns true
175
        // see https://dev.e-taxonomy.eu/redmine/issues/6780
176
        if(false && conversationHolder.isTransactionActive()){
177
            logger.trace(String.format("<<    %s comitting transaction ", _toString()));
178
            try{
179
                conversationHolder.commit(false);
180
            } catch (IllegalTransactionStateException | IllegalStateException e){
181
                // log this exception, but stop from propagating
182
                // FIXME remove this catch once https://dev.e-taxonomy.eu/redmine/issues/6780 is fixed
183
                logger.error(e.getMessage());
184
            }
185
        }
186
        conversationBound = false;
187
    }
188

    
189
    /**
190
	 * Extending classes should overwrite this method in order to perform logic
191
	 * after presenter has finished initializing.
192
	 */
193
	protected void onPresenterReady() {
194
	    logger.trace(String.format("Presenter %s ready", _toString()));
195
	}
196

    
197
    /**
198
     * <b>ONLY USED FOR LOGGING</b> when Level==TRACE
199
     * {@inheritDoc}
200
     */
201
    @Override
202
    public void onRequestStart(VaadinRequest request){
203

    
204
        if( ! requestNeedsConversation(request) ){
205
            // ignore hartbeat, fileupload, push etc
206
            logger.trace("ignoring request:" + request.getPathInfo());
207
            return;
208
        }
209
        logger.trace("onRequestStart() " + request.getPathInfo() + " " + _toString());
210
    }
211

    
212
    /**
213
     * @param request
214
     * @return
215
     */
216
    protected boolean requestNeedsConversation(VaadinRequest request) {
217
        return !(
218
                ServletPortletHelper.isAppRequest(request) // includes published file request
219
             || ServletPortletHelper.isFileUploadRequest(request)
220
             || ServletPortletHelper.isHeartbeatRequest(request)
221
             || ServletPortletHelper.isPushRequest(request)
222
             );
223
    }
224

    
225
    @Override
226
    public void onRequestEnd(VaadinRequest request, VaadinSession session){
227

    
228
        if( ! requestNeedsConversation(request) ){
229
            // ignore hartbeat, fileupload, push etc
230
            logger.trace("ignoring request:" + request.getPathInfo());
231
            return;
232
        }
233

    
234
        // always unbind at the end of a request to clean up the threadLocal variables in the
235
        // TransactionManager. This is crucial since applications containers manage threads in a pool
236
        // and the recycled threads may still have a reference to a SessionHolder from the processing
237
        // of a former request
238
        logger.trace("onRequestEnd() " + request.getPathInfo() + " " + _toString());
239
        if(conversationBound){
240
            unbindConversation();
241
        }
242
    }
243

    
244
    public final void onViewEnter() {
245
	    logger.trace(String.format("%s onViewEnter()", _toString()));
246
	    handleViewEntered();
247
	}
248

    
249
	public final void onViewExit() {
250
	    logger.trace(String.format("%s onViewExit()", _toString()));
251
	    // un-register as request start and end listener
252
	    if(conversationBound){
253
    	    logger.trace(String.format("<<<<< %s onViewExit() unbind()", _toString()));
254
            conversationHolder.unbind();
255
            conversationBound = false;
256
	    }
257
        VaadinService service = UI.getCurrent().getSession().getService();
258
        if(service instanceof CdmSpringVaadinServletService){
259
            logger.trace(String.format("~~~~~ %s un-register as request listener", _toString()));
260
            ((CdmSpringVaadinServletService)service).removeRequestEndListener(this);
261
            if(logger.isTraceEnabled()){
262
                ((CdmSpringVaadinServletService)service).removeRequestStartListener(this);
263
            }
264
        } else {
265
            throw new RuntimeException("Using the CdmSpringVaadinServletService is required for proper per view conversation handling");
266
        }
267
	    handleViewExit();
268
	}
269

    
270
	/**
271
	 * Extending classes should overwrite this method to react to the event when
272
	 * user has navigated into the view that this presenter governs.
273
	 * For implementations of {@link AbstractPopupEditor AbstractPopupEditors} this is usually
274
	 * called before the data item has been bound. This order is guaranteed since popup editors
275
	 * are managed through the {@link NavigationManagerBean}
276
	 */
277
	public void handleViewEntered() {
278
	}
279

    
280
    /**
281
     * Extending classes may overwrite this method to react to
282
     * the event when user leaves the view that this presenter
283
     * governs.
284
     */
285
    public void handleViewExit() {
286
    }
287

    
288
    /**
289
     * @return the navigationManager
290
     */
291
    public NavigationManager getNavigationManager() {
292
        return navigationManager;
293
    }
294

    
295
    protected ViewScopeConversationHolder getConversationHolder(){
296
        return conversationHolder;
297
    }
298

    
299
    /**
300
     * @param repo the repo to set
301
     */
302
    protected void setRepo(CdmRepository repo) {
303
        this.repo = repo;
304
    }
305

    
306
    /**
307
     * @param navigationManager the navigationManager to set
308
     */
309
    protected void setNavigationManager(NavigationManager navigationManager) {
310
        this.navigationManager = navigationManager;
311
    }
312

    
313

    
314

    
315
}
(6-6/8)