]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java
Start making the component system more dynamic
[lgpl/argeo-commons.git] / org.argeo.cms.ui.rap / src / org / argeo / cms / 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.apache.commons.logging.Log;
16 import org.apache.commons.logging.LogFactory;
17 import org.argeo.api.NodeConstants;
18 import org.argeo.api.cms.CmsApp;
19 import org.argeo.api.cms.CmsImageManager;
20 import org.argeo.api.cms.CmsSession;
21 import org.argeo.api.cms.CmsUi;
22 import org.argeo.api.cms.CmsView;
23 import org.argeo.api.cms.UxContext;
24 import org.argeo.cms.LocaleUtils;
25 import org.argeo.cms.auth.CurrentUser;
26 import org.argeo.cms.auth.HttpRequestCallbackHandler;
27 import org.argeo.cms.osgi.CmsOsgiUtils;
28 import org.argeo.cms.servlet.ServletHttpRequest;
29 import org.argeo.cms.servlet.ServletHttpResponse;
30 import org.argeo.cms.swt.CmsSwtUtils;
31 import org.argeo.cms.swt.SimpleSwtUxContext;
32 import org.argeo.cms.swt.dialogs.CmsFeedback;
33 import org.argeo.cms.ui.util.DefaultImageManager;
34 import org.argeo.eclipse.ui.specific.UiContext;
35 import org.eclipse.rap.rwt.RWT;
36 import org.eclipse.rap.rwt.application.EntryPoint;
37 import org.eclipse.rap.rwt.client.service.BrowserNavigation;
38 import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
39 import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
40 import org.eclipse.rap.rwt.internal.lifecycle.RWTLifeCycle;
41 import org.eclipse.swt.SWT;
42 import org.eclipse.swt.SWTError;
43 import org.eclipse.swt.widgets.Composite;
44 import org.eclipse.swt.widgets.Display;
45 import org.eclipse.swt.widgets.Shell;
46 import org.osgi.service.event.Event;
47 import org.osgi.service.event.EventAdmin;
48
49 /** The {@link CmsView} for a {@link CmsWebApp}. */
50 @SuppressWarnings("restriction")
51 public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationListener {
52 private static final long serialVersionUID = 7733510691684570402L;
53 private final static Log log = LogFactory.getLog(CmsWebEntryPoint.class);
54
55 private EventAdmin eventAdmin;
56
57 private final CmsWebApp cmsWebApp;
58 private final String uiName;
59
60 private LoginContext loginContext;
61 private String state;
62 private Throwable exception;
63 private UxContext uxContext;
64 private CmsImageManager imageManager;
65
66 private Display display;
67 private CmsUi ui;
68
69 private String uid;
70
71 // Client services
72 // private final JavaScriptExecutor jsExecutor;
73 private final BrowserNavigation browserNavigation;
74
75 /** Experimental OS-like multi windows. */
76 private boolean multipleShells = false;
77
78 public CmsWebEntryPoint(CmsWebApp cmsWebApp, String uiName) {
79 assert cmsWebApp != null;
80 assert uiName != null;
81 this.cmsWebApp = cmsWebApp;
82 this.uiName = uiName;
83 uid = UUID.randomUUID().toString();
84
85 // Initial login
86 LoginContext lc;
87 try {
88 lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER,
89 new HttpRequestCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
90 new ServletHttpResponse(UiContext.getHttpResponse())));
91 lc.login();
92 } catch (LoginException e) {
93 try {
94 lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_ANONYMOUS);
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(NodeConstants.LOGIN_CONTEXT_ANONYMOUS);
160 anonymousLc.login();
161 authChange(anonymousLc);
162 } catch (LoginException e) {
163 log.error("Cannot logout", e);
164 }
165 }
166
167 @Override
168 public synchronized void authChange(LoginContext lc) {
169 if (lc == null)
170 throw new IllegalArgumentException("Login context cannot be null");
171 // logout previous login context
172 if (this.loginContext != null)
173 try {
174 this.loginContext.logout();
175 } catch (LoginException e1) {
176 log.warn("Could not log out: " + e1);
177 }
178 this.loginContext = lc;
179 doRefresh();
180 }
181
182 @Override
183 public void exception(final Throwable e) {
184 if (e instanceof SWTError) {
185 SWTError swtError = (SWTError) e;
186 if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED)
187 return;
188 }
189 display.syncExec(() -> {
190 // CmsFeedback.show("Unexpected exception in CMS", e);
191 exception = e;
192 // log.error("Unexpected exception in CMS", e);
193 doRefresh();
194 });
195 }
196
197 protected synchronized void doRefresh() {
198 if (ui != null)
199 Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
200 @Override
201 public Void run() {
202 if (exception != null) {
203 // TODO internationalise
204 CmsFeedback.show("Unexpected exception", exception);
205 exception = null;
206 // TODO report
207 }
208 cmsWebApp.getCmsApp().refreshUi(ui, state);
209 return null;
210 }
211 });
212 }
213
214 /** Sets the state of the entry point and retrieve the related JCR node. */
215 protected String setState(String newState) {
216 cmsWebApp.getCmsApp().setState(ui, newState);
217 state = newState;
218 return null;
219 }
220
221 @Override
222 public UxContext getUxContext() {
223 return uxContext;
224 }
225
226 @Override
227 public String getUid() {
228 return uid;
229 }
230
231 @Override
232 public void navigateTo(String state) {
233 exception = null;
234 String title = setState(state);
235 if (title != null)
236 doRefresh();
237 if (browserNavigation != null)
238 browserNavigation.pushState(state, title);
239 }
240
241 public CmsImageManager getImageManager() {
242 return imageManager;
243 }
244
245 @Override
246 public void navigated(BrowserNavigationEvent event) {
247 setState(event.getState());
248 // doRefresh();
249 }
250
251 @Override
252 public void sendEvent(String topic, Map<String, Object> properties) {
253 if (properties == null)
254 properties = new HashMap<>();
255 if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid))
256 throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid ("
257 + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid);
258 properties.put(CMS_VIEW_UID_PROPERTY, uid);
259 eventAdmin.sendEvent(new Event(topic, properties));
260 }
261
262 @Override
263 public void stateChanged(String state, String title) {
264 browserNavigation.pushState(state, title);
265 }
266
267 @Override
268 public CmsSession getCmsSession() {
269 CmsSession cmsSession = CmsOsgiUtils.getCmsSession(cmsWebApp.getBundleContext(), getSubject());
270 return cmsSession;
271 }
272
273 @Override
274 public Object getData(String key) {
275 if (ui != null) {
276 return ui.getData(key);
277 } else {
278 throw new IllegalStateException("UI is not initialized");
279 }
280 }
281
282 @Override
283 public void setData(String key, Object value) {
284 if (ui != null) {
285 ui.setData(key, value);
286 } else {
287 throw new IllegalStateException("UI is not initialized");
288 }
289 }
290
291 /*
292 * EntryPoint IMPLEMENTATION
293 */
294
295 @Override
296 public int createUI() {
297 Display display = new Display();
298 Shell shell = createShell(display);
299 shell.setLayout(CmsSwtUtils.noSpaceGridLayout());
300 CmsSwtUtils.registerCmsView(shell, this);
301 createContents(shell);
302 shell.layout();
303 // if (shell.getMaximized()) {
304 // shell.layout();
305 // } else {
306 //// shell.pack();
307 // }
308 shell.open();
309 if (getApplicationContext().getLifeCycleFactory().getLifeCycle() instanceof RWTLifeCycle) {
310 eventLoop: while (!shell.isDisposed()) {
311 try {
312 if (!display.readAndDispatch()) {
313 display.sleep();
314 }
315 } catch (Throwable e) {
316 if (e instanceof SWTError) {
317 SWTError swtError = (SWTError) e;
318 if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED) {
319 log.error("Unexpected SWT error in event loop, ignoring it. " + e.getMessage());
320 continue eventLoop;
321 } else {
322 log.error("Unexpected SWT error in event loop, shutting down...", e);
323 break eventLoop;
324 }
325 } else if (e instanceof ThreadDeath) {
326 throw (ThreadDeath) e;
327 } else if (e instanceof Error) {
328 log.error("Unexpected error in event loop, shutting down...", e);
329 break eventLoop;
330 } else {
331 log.error("Unexpected exception in event loop, ignoring it. " + e.getMessage());
332 continue eventLoop;
333 }
334 }
335 }
336 if (!display.isDisposed())
337 display.dispose();
338 }
339 return 0;
340 }
341
342 protected Shell createShell(Display display) {
343 Shell shell;
344 if (!multipleShells) {
345 shell = new Shell(display, SWT.NO_TRIM);
346 shell.setMaximized(true);
347 } else {
348 shell = new Shell(display, SWT.SHELL_TRIM);
349 shell.setSize(800, 600);
350 }
351 return shell;
352 }
353
354 public void setEventAdmin(EventAdmin eventAdmin) {
355 this.eventAdmin = eventAdmin;
356 }
357
358 }