]> git.argeo.org Git - lgpl/argeo-commons.git/blob - web/CmsWebEntryPoint.java
Prepare next development cycle
[lgpl/argeo-commons.git] / web / CmsWebEntryPoint.java
1 package org.argeo.cms.web;
2
3 import static org.eclipse.rap.rwt.internal.service.ContextProvider.getApplicationContext;
4
5 import java.security.PrivilegedAction;
6 import java.util.HashMap;
7 import java.util.Locale;
8 import java.util.Map;
9 import java.util.UUID;
10
11 import javax.security.auth.Subject;
12 import javax.security.auth.login.LoginContext;
13 import javax.security.auth.login.LoginException;
14
15 import org.argeo.api.cms.CmsApp;
16 import org.argeo.api.cms.CmsAuth;
17 import org.argeo.api.cms.CmsLog;
18 import org.argeo.api.cms.CmsSession;
19 import org.argeo.api.cms.ux.CmsImageManager;
20 import org.argeo.api.cms.ux.CmsUi;
21 import org.argeo.api.cms.ux.CmsView;
22 import org.argeo.api.cms.ux.UxContext;
23 import org.argeo.cms.LocaleUtils;
24 import org.argeo.cms.auth.CurrentUser;
25 import org.argeo.cms.auth.RemoteAuthCallbackHandler;
26 import org.argeo.cms.servlet.ServletHttpRequest;
27 import org.argeo.cms.servlet.ServletHttpResponse;
28 import org.argeo.cms.swt.CmsSwtUtils;
29 import org.argeo.cms.swt.SimpleSwtUxContext;
30 import org.argeo.cms.swt.dialogs.CmsFeedback;
31 import org.argeo.cms.ui.util.DefaultImageManager;
32 import org.argeo.eclipse.ui.specific.UiContext;
33 import org.eclipse.rap.rwt.RWT;
34 import org.eclipse.rap.rwt.application.EntryPoint;
35 import org.eclipse.rap.rwt.client.service.BrowserNavigation;
36 import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
37 import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
38 import org.eclipse.rap.rwt.internal.lifecycle.RWTLifeCycle;
39 import org.eclipse.swt.SWT;
40 import org.eclipse.swt.SWTError;
41 import org.eclipse.swt.widgets.Composite;
42 import org.eclipse.swt.widgets.Display;
43 import org.eclipse.swt.widgets.Shell;
44 import org.osgi.service.event.Event;
45 import org.osgi.service.event.EventAdmin;
46
47 /** The {@link CmsView} for a {@link CmsWebApp}. */
48 @SuppressWarnings("restriction")
49 public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationListener {
50 private static final long serialVersionUID = 7733510691684570402L;
51 private final static CmsLog log = CmsLog.getLog(CmsWebEntryPoint.class);
52
53 private EventAdmin eventAdmin;
54
55 private final CmsWebApp cmsWebApp;
56 private final String uiName;
57
58 private LoginContext loginContext;
59 private String state;
60 private Throwable exception;
61 private UxContext uxContext;
62 private CmsImageManager imageManager;
63
64 private Display display;
65 private CmsUi ui;
66
67 private String uid;
68
69 // Client services
70 // private final JavaScriptExecutor jsExecutor;
71 private final BrowserNavigation browserNavigation;
72
73 /** Experimental OS-like multi windows. */
74 private boolean multipleShells = false;
75
76 public CmsWebEntryPoint(CmsWebApp cmsWebApp, String uiName) {
77 assert cmsWebApp != null;
78 assert uiName != null;
79 this.cmsWebApp = cmsWebApp;
80 this.uiName = uiName;
81 uid = UUID.randomUUID().toString();
82
83 // Initial login
84 LoginContext lc;
85 try {
86 lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER,
87 new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
88 new ServletHttpResponse(UiContext.getHttpResponse())));
89 lc.login();
90 } catch (LoginException e) {
91 try {
92 lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS,
93 new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
94 new ServletHttpResponse(UiContext.getHttpResponse())));
95 lc.login();
96 } catch (LoginException e1) {
97 throw new IllegalStateException("Cannot log in as anonymous", e1);
98 }
99 }
100 authChange(lc);
101
102 // jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
103 browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
104 if (browserNavigation != null)
105 browserNavigation.addBrowserNavigationListener(this);
106 }
107
108 protected void createContents(Composite parent) {
109 Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Void>() {
110 @Override
111 public Void run() {
112 try {
113 uxContext = new SimpleSwtUxContext();
114 imageManager = new DefaultImageManager();
115 CmsSession cmsSession = getCmsSession();
116 if (cmsSession != null) {
117 UiContext.setLocale(cmsSession.getLocale());
118 LocaleUtils.setThreadLocale(cmsSession.getLocale());
119 } else {
120 Locale rwtLocale = RWT.getUISession().getLocale();
121 LocaleUtils.setThreadLocale(rwtLocale);
122 }
123 parent.setData(CmsApp.UI_NAME_PROPERTY, uiName);
124 display = parent.getDisplay();
125 ui = cmsWebApp.getCmsApp().initUi(parent);
126 if (ui instanceof Composite)
127 ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll());
128 // we need ui to be set before refresh so that CmsView can store UI context data
129 // in it.
130 cmsWebApp.getCmsApp().refreshUi(ui, null);
131 } catch (Exception e) {
132 throw new IllegalStateException("Cannot create entrypoint contents", e);
133 }
134 return null;
135 }
136 });
137 }
138
139 protected Subject getSubject() {
140 return loginContext.getSubject();
141 }
142
143 public <T> T doAs(PrivilegedAction<T> action) {
144 return Subject.doAs(getSubject(), action);
145 }
146
147 @Override
148 public boolean isAnonymous() {
149 return CurrentUser.isAnonymous(getSubject());
150 }
151
152 @Override
153 public synchronized void logout() {
154 if (loginContext == null)
155 throw new IllegalArgumentException("Login context should not be null");
156 try {
157 CurrentUser.logoutCmsSession(loginContext.getSubject());
158 loginContext.logout();
159 LoginContext anonymousLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS,
160 new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
161 new ServletHttpResponse(UiContext.getHttpResponse())));
162 anonymousLc.login();
163 authChange(anonymousLc);
164 } catch (LoginException e) {
165 log.error("Cannot logout", e);
166 }
167 }
168
169 @Override
170 public synchronized void authChange(LoginContext lc) {
171 if (lc == null)
172 throw new IllegalArgumentException("Login context cannot be null");
173 // logout previous login context
174 if (this.loginContext != null)
175 try {
176 this.loginContext.logout();
177 } catch (LoginException e1) {
178 log.warn("Could not log out: " + e1);
179 }
180 this.loginContext = lc;
181 doRefresh();
182 }
183
184 @Override
185 public void exception(final Throwable e) {
186 if (e instanceof SWTError) {
187 SWTError swtError = (SWTError) e;
188 if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED)
189 return;
190 }
191 display.syncExec(() -> {
192 // CmsFeedback.show("Unexpected exception in CMS", e);
193 exception = e;
194 log.error("Unexpected exception in CMS", e);
195 doRefresh();
196 });
197 }
198
199 protected synchronized void doRefresh() {
200 if (ui != null)
201 Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
202 @Override
203 public Void run() {
204 if (exception != null) {
205 // TODO internationalise
206 CmsFeedback.show("Unexpected exception", exception);
207 exception = null;
208 // TODO report
209 }
210 cmsWebApp.getCmsApp().refreshUi(ui, state);
211 return null;
212 }
213 });
214 }
215
216 /** Sets the state of the entry point and retrieve the related JCR node. */
217 protected String setState(String newState) {
218 cmsWebApp.getCmsApp().setState(ui, newState);
219 state = newState;
220 return null;
221 }
222
223 @Override
224 public UxContext getUxContext() {
225 return uxContext;
226 }
227
228 @Override
229 public String getUid() {
230 return uid;
231 }
232
233 @Override
234 public void navigateTo(String state) {
235 exception = null;
236 String title = setState(state);
237 if (title != null)
238 doRefresh();
239 if (browserNavigation != null)
240 browserNavigation.pushState(state, title);
241 }
242
243 public CmsImageManager getImageManager() {
244 return imageManager;
245 }
246
247 @Override
248 public void navigated(BrowserNavigationEvent event) {
249 setState(event.getState());
250 // doRefresh();
251 }
252
253 @Override
254 public void sendEvent(String topic, Map<String, Object> properties) {
255 if (properties == null)
256 properties = new HashMap<>();
257 if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid))
258 throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid ("
259 + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid);
260 properties.put(CMS_VIEW_UID_PROPERTY, uid);
261 eventAdmin.sendEvent(new Event(topic, properties));
262 }
263
264 @Override
265 public void stateChanged(String state, String title) {
266 browserNavigation.pushState(state, title);
267 }
268
269 @Override
270 public CmsSession getCmsSession() {
271 CmsSession cmsSession = cmsWebApp.getCmsApp().getCmsContext().getCmsSession(getSubject());
272 if (cmsSession == null)
273 throw new IllegalStateException("No CMS session available for " + getSubject());
274 return cmsSession;
275 }
276
277 @Override
278 public Object getData(String key) {
279 if (ui != null) {
280 return ui.getData(key);
281 } else {
282 throw new IllegalStateException("UI is not initialized");
283 }
284 }
285
286 @Override
287 public void setData(String key, Object value) {
288 if (ui != null) {
289 ui.setData(key, value);
290 } else {
291 throw new IllegalStateException("UI is not initialized");
292 }
293 }
294
295 /*
296 * EntryPoint IMPLEMENTATION
297 */
298
299 @Override
300 public int createUI() {
301 Display display = new Display();
302 Shell shell = createShell(display);
303 shell.setLayout(CmsSwtUtils.noSpaceGridLayout());
304 CmsSwtUtils.registerCmsView(shell, this);
305 createContents(shell);
306 shell.layout();
307 // if (shell.getMaximized()) {
308 // shell.layout();
309 // } else {
310 //// shell.pack();
311 // }
312 shell.open();
313 if (getApplicationContext().getLifeCycleFactory().getLifeCycle() instanceof RWTLifeCycle) {
314 eventLoop: while (!shell.isDisposed()) {
315 try {
316 if (!display.readAndDispatch()) {
317 display.sleep();
318 }
319 } catch (Throwable e) {
320 if (e instanceof SWTError) {
321 SWTError swtError = (SWTError) e;
322 if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED) {
323 log.error("Unexpected SWT error in event loop, ignoring it. " + e.getMessage());
324 continue eventLoop;
325 } else {
326 log.error("Unexpected SWT error in event loop, shutting down...", e);
327 break eventLoop;
328 }
329 } else if (e instanceof ThreadDeath) {
330 throw (ThreadDeath) e;
331 } else if (e instanceof Error) {
332 log.error("Unexpected error in event loop, shutting down...", e);
333 break eventLoop;
334 } else {
335 log.error("Unexpected exception in event loop, ignoring it. " + e.getMessage());
336 continue eventLoop;
337 }
338 }
339 }
340 if (!display.isDisposed())
341 display.dispose();
342 }
343 return 0;
344 }
345
346 protected Shell createShell(Display display) {
347 Shell shell;
348 if (!multipleShells) {
349 shell = new Shell(display, SWT.NO_TRIM);
350 shell.setMaximized(true);
351 } else {
352 shell = new Shell(display, SWT.SHELL_TRIM);
353 shell.setSize(800, 600);
354 }
355 return shell;
356 }
357
358 public void setEventAdmin(EventAdmin eventAdmin) {
359 this.eventAdmin = eventAdmin;
360 }
361
362 }