]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java
Use Argeo TP v2.1.10
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / AbstractCmsEntryPoint.java
1 package org.argeo.cms;
2
3 import java.security.PrivilegedAction;
4 import java.util.HashMap;
5 import java.util.Locale;
6 import java.util.Map;
7 import java.util.ResourceBundle;
8
9 import javax.jcr.Node;
10 import javax.jcr.Property;
11 import javax.jcr.Repository;
12 import javax.jcr.RepositoryException;
13 import javax.jcr.Session;
14 import javax.jcr.nodetype.NodeType;
15 import javax.security.auth.Subject;
16 import javax.security.auth.login.LoginException;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.argeo.ArgeoException;
21 import org.argeo.cms.auth.ArgeoLoginContext;
22 import org.argeo.cms.auth.LoginRequiredException;
23 import org.argeo.cms.i18n.Msg;
24 import org.argeo.jcr.JcrUtils;
25 import org.eclipse.rap.rwt.RWT;
26 import org.eclipse.rap.rwt.application.AbstractEntryPoint;
27 import org.eclipse.rap.rwt.client.WebClient;
28 import org.eclipse.rap.rwt.client.service.BrowserNavigation;
29 import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
30 import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
31 import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
32 import org.eclipse.swt.widgets.Composite;
33 import org.eclipse.swt.widgets.Display;
34 import org.eclipse.swt.widgets.Shell;
35
36 /** Manages history and navigation */
37 public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint
38 implements CmsSession {
39 private final Log log = LogFactory.getLog(AbstractCmsEntryPoint.class);
40
41 private final Subject subject = new Subject();
42
43 private final Repository repository;
44 private final String workspace;
45 private final String defaultPath;
46 private final Map<String, String> factoryProperties;
47
48 // Current state
49 private Session session;
50 private Node node;
51 private String state;
52 private String page;
53 private Throwable exception;
54
55 // Client services
56 private final JavaScriptExecutor jsExecutor;
57 private final BrowserNavigation browserNavigation;
58
59 public AbstractCmsEntryPoint(Repository repository, String workspace,
60 String defaultPath, Map<String, String> factoryProperties) {
61 this.repository = repository;
62 this.workspace = workspace;
63 this.defaultPath = defaultPath;
64 this.factoryProperties = new HashMap<String, String>(factoryProperties);
65
66 // Initial login
67 try {
68 new ArgeoLoginContext(KernelHeader.LOGIN_CONTEXT_USER, subject)
69 .login();
70 } catch (LoginException e) {
71 // if (log.isTraceEnabled())
72 // log.trace("Cannot authenticate user", e);
73 try {
74 new ArgeoLoginContext(KernelHeader.LOGIN_CONTEXT_ANONYMOUS,
75 subject).login();
76 } catch (LoginException eAnonymous) {
77 throw new ArgeoException("Cannot initialize subject",
78 eAnonymous);
79 }
80 }
81 authChange();
82
83 jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
84 browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
85 if (browserNavigation != null)
86 browserNavigation
87 .addBrowserNavigationListener(new CmsNavigationListener());
88 }
89
90 @Override
91 protected Shell createShell(Display display) {
92 Shell shell = super.createShell(display);
93 shell.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_SHELL);
94 display.disposeExec(new Runnable() {
95
96 @Override
97 public void run() {
98 if (log.isTraceEnabled())
99 log.trace("Logging out " + session);
100 JcrUtils.logoutQuietly(session);
101 }
102 });
103 return shell;
104 }
105
106 @Override
107 protected final void createContents(final Composite parent) {
108 getShell().getDisplay().setData(CmsSession.KEY, this);
109 Subject.doAs(subject, new PrivilegedAction<Void>() {
110 @Override
111 public Void run() {
112 try {
113 createUi(parent);
114 } catch (Exception e) {
115 throw new CmsException("Cannot create entrypoint contents",
116 e);
117 }
118 return null;
119 }
120 });
121 }
122
123 /** Create UI */
124 protected abstract void createUi(Composite parent);
125
126 /** Recreate UI after navigation or auth change */
127 protected abstract void refresh();
128
129 /**
130 * The node to return when no node was found (for authenticated users and
131 * anonymous)
132 */
133 protected Node getDefaultNode(Session session) throws RepositoryException {
134 if (!session.hasPermission(defaultPath, "read")) {
135 if (session.getUserID().equals("anonymous"))
136 throw new LoginRequiredException();
137 else
138 throw new CmsException("Unauthorized");
139 }
140 return session.getNode(defaultPath);
141 }
142
143 protected String getBaseTitle() {
144 return factoryProperties.get(WebClient.PAGE_TITLE);
145 }
146
147 public void navigateTo(String state) {
148 exception = null;
149 String title = setState(state);
150 doRefresh();
151 if (browserNavigation != null)
152 browserNavigation.pushState(state, title);
153 }
154
155 @Override
156 public Subject getSubject() {
157 return subject;
158 }
159
160 @Override
161 public void authChange() {
162 Subject.doAs(subject, new PrivilegedAction<Void>() {
163
164 @Override
165 public Void run() {
166 try {
167 String currentPath = null;
168 if (node != null)
169 currentPath = node.getPath();
170 JcrUtils.logoutQuietly(session);
171
172 session = repository.login(workspace);
173 if (currentPath != null)
174 try {
175 node = session.getNode(currentPath);
176 } catch (Exception e) {
177 try {
178 // TODO find a less hacky way to log out
179 new ArgeoLoginContext(
180 KernelHeader.LOGIN_CONTEXT_ANONYMOUS,
181 subject).logout();
182 new ArgeoLoginContext(
183 KernelHeader.LOGIN_CONTEXT_ANONYMOUS,
184 subject).login();
185 } catch (LoginException eAnonymous) {
186 throw new ArgeoException(
187 "Cannot reset to anonymous", eAnonymous);
188 }
189 JcrUtils.logoutQuietly(session);
190 session = repository.login(workspace);
191 navigateTo("~");
192 throw e;
193 }
194
195 // refresh UI
196 doRefresh();
197 } catch (RepositoryException e) {
198 throw new CmsException("Cannot perform auth change", e);
199 }
200 return null;
201 }
202
203 });
204
205 }
206
207 @Override
208 public void exception(final Throwable e) {
209 AbstractCmsEntryPoint.this.exception = e;
210 log.error("Unexpected exception in CMS", e);
211 doRefresh();
212 }
213
214 protected void doRefresh() {
215 Subject.doAs(subject, new PrivilegedAction<Void>() {
216 @Override
217 public Void run() {
218 refresh();
219 return null;
220 }
221 });
222 }
223
224 @Override
225 public Object local(Msg msg) {
226 String key = msg.getId();
227 int lastDot = key.lastIndexOf('.');
228 String className = key.substring(0, lastDot);
229 String fieldName = key.substring(lastDot + 1);
230 Locale locale = RWT.getLocale();
231 ResourceBundle rb = ResourceBundle.getBundle(className, locale,
232 msg.getClassLoader());
233 return rb.getString(fieldName);
234 }
235
236 /** Sets the state of the entry point and retrieve the related JCR node. */
237 protected synchronized String setState(String newState) {
238 String previousState = this.state;
239
240 node = null;
241 page = null;
242 this.state = newState;
243 if (newState.equals("~"))
244 this.state = "";
245
246 try {
247 int firstSlash = state.indexOf('/');
248 if (firstSlash == 0) {
249 if (session.nodeExists(state))
250 node = session.getNode(state);
251 else
252 throw new CmsException("Data " + state + " does not exist");
253 page = "";
254 } else if (firstSlash > 0) {
255 String prefix = state.substring(0, firstSlash);
256 String path = state.substring(firstSlash);
257 if (session.nodeExists(path))
258 node = session.getNode(path);
259 else
260 throw new CmsException("Data " + path + " does not exist");
261 page = prefix;
262 } else {
263 node = getDefaultNode(session);
264 page = state;
265 }
266
267 // Title
268 String title;
269 if (node.isNodeType(NodeType.MIX_TITLE)
270 && node.hasProperty(Property.JCR_TITLE))
271 title = node.getProperty(Property.JCR_TITLE).getString()
272 + " - " + getBaseTitle();
273 else
274 title = getBaseTitle();
275 jsExecutor.execute("document.title = \"" + title + "\"");
276
277 if (log.isTraceEnabled())
278 log.trace("node=" + node + ", state=" + state + " (page="
279 + page + ", title=" + title + ")");
280
281 return title;
282 } catch (Exception e) {
283 log.error("Cannot set state '" + state + "'", e);
284 if (previousState.equals(""))
285 previousState = "~";
286 navigateTo(previousState);
287 throw new CmsException("Unexpected issue when accessing #"
288 + newState, e);
289 }
290 }
291
292 protected Node getNode() {
293 return node;
294 }
295
296 protected String getState() {
297 return state;
298 }
299
300 protected Throwable getException() {
301 return exception;
302 }
303
304 protected void resetException() {
305 exception = null;
306 }
307
308 protected Session getSession() {
309 return session;
310 }
311
312 private class CmsNavigationListener implements BrowserNavigationListener {
313 private static final long serialVersionUID = -3591018803430389270L;
314
315 @Override
316 public void navigated(BrowserNavigationEvent event) {
317 setState(event.getState());
318 refresh();
319 }
320 }
321 }