]> git.argeo.org Git - lgpl/argeo-commons.git/blob - SimpleApp.java
b75c700078d7bff8a81840390a94f3a99c2c30da
[lgpl/argeo-commons.git] / SimpleApp.java
1 package org.argeo.cms.util;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.net.URL;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Enumeration;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.Hashtable;
12 import java.util.LinkedHashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16
17 import javax.jcr.Repository;
18 import javax.jcr.RepositoryException;
19 import javax.jcr.Session;
20 import javax.jcr.security.Privilege;
21 import javax.jcr.version.VersionManager;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.argeo.cms.CmsException;
26 import org.argeo.cms.ui.CmsConstants;
27 import org.argeo.cms.ui.CmsUiProvider;
28 import org.argeo.cms.ui.LifeCycleUiProvider;
29 import org.argeo.jcr.JcrUtils;
30 import org.argeo.node.NodeConstants;
31 import org.eclipse.rap.rwt.RWT;
32 import org.eclipse.rap.rwt.application.Application;
33 import org.eclipse.rap.rwt.application.Application.OperationMode;
34 import org.eclipse.rap.rwt.application.ApplicationConfiguration;
35 import org.eclipse.rap.rwt.application.EntryPoint;
36 import org.eclipse.rap.rwt.application.EntryPointFactory;
37 import org.eclipse.rap.rwt.application.ExceptionHandler;
38 import org.eclipse.rap.rwt.client.WebClient;
39 import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
40 import org.eclipse.rap.rwt.service.ResourceLoader;
41 import org.eclipse.swt.SWT;
42 import org.eclipse.swt.events.SelectionAdapter;
43 import org.eclipse.swt.events.SelectionEvent;
44 import org.eclipse.swt.layout.FillLayout;
45 import org.eclipse.swt.widgets.Button;
46 import org.eclipse.swt.widgets.Composite;
47 import org.osgi.framework.Bundle;
48 import org.osgi.framework.BundleContext;
49 import org.osgi.framework.ServiceRegistration;
50
51 /** A basic generic app based on {@link SimpleErgonomics}. */
52 public class SimpleApp implements CmsConstants, ApplicationConfiguration {
53 private final static Log log = LogFactory.getLog(SimpleApp.class);
54
55 private String contextName = null;
56
57 private Map<String, Map<String, String>> branding = new HashMap<String, Map<String, String>>();
58 private Map<String, List<String>> styleSheets = new HashMap<String, List<String>>();
59
60 private List<String> resources = new ArrayList<String>();
61
62 private BundleContext bundleContext;
63
64 private Repository repository;
65 private String workspace = null;
66 private String jcrBasePath = "/";
67 private List<String> roPrincipals = Arrays.asList(NodeConstants.ROLE_ANONYMOUS, NodeConstants.ROLE_USER);
68 private List<String> rwPrincipals = Arrays.asList(NodeConstants.ROLE_USER);
69
70 private CmsUiProvider header;
71 private Map<String, CmsUiProvider> pages = new LinkedHashMap<String, CmsUiProvider>();
72
73 private Integer headerHeight = 40;
74
75 private ServiceRegistration<ApplicationConfiguration> appReg;
76
77 public void configure(Application application) {
78 try {
79 BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle());
80
81 application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
82 // application.setOperationMode(OperationMode.JEE_COMPATIBILITY);
83
84 application.setExceptionHandler(new CmsExceptionHandler());
85
86 // loading animated gif
87 application.addResource(LOADING_IMAGE, createResourceLoader(LOADING_IMAGE));
88 // empty image
89 application.addResource(NO_IMAGE, createResourceLoader(NO_IMAGE));
90
91 for (String resource : resources) {
92 application.addResource(resource, bundleRL);
93 if (log.isTraceEnabled())
94 log.trace("Resource " + resource);
95 }
96
97 Map<String, String> defaultBranding = null;
98 if (branding.containsKey("*"))
99 defaultBranding = branding.get("*");
100 String defaultTheme = defaultBranding.get(WebClient.THEME_ID);
101
102 // entry points
103 for (String page : pages.keySet()) {
104 Map<String, String> properties = defaultBranding != null ? new HashMap<String, String>(defaultBranding)
105 : new HashMap<String, String>();
106 if (branding.containsKey(page)) {
107 properties.putAll(branding.get(page));
108 }
109 // favicon
110 if (properties.containsKey(WebClient.FAVICON)) {
111 String themeId = defaultBranding.get(WebClient.THEME_ID);
112 Bundle themeBundle = findThemeBundle(themeId);
113 String faviconRelPath = properties.get(WebClient.FAVICON);
114 application.addResource(faviconRelPath,
115 new BundleResourceLoader(themeBundle != null ? themeBundle : bundleContext.getBundle()));
116 if (log.isTraceEnabled())
117 log.trace("Favicon " + faviconRelPath);
118
119 }
120
121 // page title
122 if (!properties.containsKey(WebClient.PAGE_TITLE)) {
123 if (page.length() > 0)
124 properties.put(WebClient.PAGE_TITLE, Character.toUpperCase(page.charAt(0)) + page.substring(1));
125 }
126
127 // default body HTML
128 if (!properties.containsKey(WebClient.BODY_HTML))
129 properties.put(WebClient.BODY_HTML, DEFAULT_LOADING_BODY);
130
131 //
132 // ADD ENTRY POINT
133 //
134 application.addEntryPoint("/" + page,
135 new CmsEntryPointFactory(pages.get(page), repository, workspace, properties), properties);
136 log.info("Page /" + page);
137 }
138
139 // stylesheets and themes
140 Set<Bundle> themeBundles = new HashSet<>();
141 for (String themeId : styleSheets.keySet()) {
142 Bundle themeBundle = findThemeBundle(themeId);
143 StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader(
144 themeBundle != null ? themeBundle : bundleContext.getBundle());
145 if (themeBundle != null)
146 themeBundles.add(themeBundle);
147 List<String> cssLst = styleSheets.get(themeId);
148 if (log.isDebugEnabled())
149 log.debug("Theme " + themeId);
150 for (String css : cssLst) {
151 application.addStyleSheet(themeId, css, styleSheetRL);
152 if (log.isTraceEnabled())
153 log.trace(" CSS " + css);
154 }
155
156 }
157 for (Bundle themeBundle : themeBundles) {
158 BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle);
159 addThemeResources(application, themeBundle, themeBRL, "*.png");
160 addThemeResources(application, themeBundle, themeBRL, "*.gif");
161 addThemeResources(application, themeBundle, themeBRL, "*.jpg");
162 }
163 } catch (RuntimeException e) {
164 // Easier access to initialisation errors
165 log.error("Unexpected exception when configuring RWT application.", e);
166 throw e;
167 }
168 }
169
170 private Bundle findThemeBundle(String themeId) {
171 if (themeId == null)
172 return null;
173 // TODO optimize
174 // TODO deal with multiple versions
175 Bundle themeBundle = null;
176 if (themeId != null) {
177 for (Bundle bundle : bundleContext.getBundles())
178 if (themeId.equals(bundle.getSymbolicName())) {
179 themeBundle = bundle;
180 break;
181 }
182 }
183 return themeBundle;
184 }
185
186 private void addThemeResources(Application application, Bundle themeBundle, BundleResourceLoader themeBRL,
187 String pattern) {
188 Enumeration<URL> themeResources = themeBundle.findEntries("/", pattern, true);
189 if (themeResources == null)
190 return;
191 while (themeResources.hasMoreElements()) {
192 String resource = themeResources.nextElement().getPath();
193 // remove first '/' so that RWT registers it
194 resource = resource.substring(1);
195 if (!resource.endsWith("/")) {
196 application.addResource(resource, themeBRL);
197 if (log.isTraceEnabled())
198 log.trace("Registered " + resource + " from theme " + themeBundle);
199 }
200
201 }
202
203 }
204
205 public void init() throws RepositoryException {
206 Session session = null;
207 try {
208 session = JcrUtils.loginOrCreateWorkspace(repository, workspace);
209 VersionManager vm = session.getWorkspace().getVersionManager();
210 JcrUtils.mkdirs(session, jcrBasePath);
211 session.save();
212 if (!vm.isCheckedOut(jcrBasePath))
213 vm.checkout(jcrBasePath);
214 for (String principal : rwPrincipals)
215 JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_WRITE);
216 for (String principal : roPrincipals)
217 JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_READ);
218
219 for (String pageName : pages.keySet()) {
220 try {
221 initPage(session, pages.get(pageName));
222 session.save();
223 } catch (Exception e) {
224 throw new CmsException("Cannot initialize page " + pageName, e);
225 }
226 }
227
228 } finally {
229 JcrUtils.logoutQuietly(session);
230 }
231
232 // publish to OSGi
233 register();
234 }
235
236 protected void initPage(Session adminSession, CmsUiProvider page) throws RepositoryException {
237 if (page instanceof LifeCycleUiProvider)
238 ((LifeCycleUiProvider) page).init(adminSession);
239 }
240
241 public void destroy() {
242 for (String pageName : pages.keySet()) {
243 try {
244 CmsUiProvider page = pages.get(pageName);
245 if (page instanceof LifeCycleUiProvider)
246 ((LifeCycleUiProvider) page).destroy();
247 } catch (Exception e) {
248 log.error("Cannot destroy page " + pageName, e);
249 }
250 }
251 }
252
253 protected void register() {
254 Hashtable<String, String> props = new Hashtable<String, String>();
255 if (contextName != null)
256 props.put("contextName", contextName);
257 appReg = bundleContext.registerService(ApplicationConfiguration.class, this, props);
258 if (log.isDebugEnabled())
259 log.debug("Registered " + (contextName == null ? "/" : contextName));
260 }
261
262 protected void unregister() {
263 appReg.unregister();
264 if (log.isDebugEnabled())
265 log.debug("Unregistered " + (contextName == null ? "/" : contextName));
266 }
267
268 public void setRepository(Repository repository) {
269 this.repository = repository;
270 }
271
272 public void setWorkspace(String workspace) {
273 this.workspace = workspace;
274 }
275
276 public void setHeader(CmsUiProvider header) {
277 this.header = header;
278 }
279
280 public void setPages(Map<String, CmsUiProvider> pages) {
281 this.pages = pages;
282 }
283
284 public void setJcrBasePath(String basePath) {
285 this.jcrBasePath = basePath;
286 }
287
288 public void setRoPrincipals(List<String> roPrincipals) {
289 this.roPrincipals = roPrincipals;
290 }
291
292 public void setRwPrincipals(List<String> rwPrincipals) {
293 this.rwPrincipals = rwPrincipals;
294 }
295
296 public void setHeaderHeight(Integer headerHeight) {
297 this.headerHeight = headerHeight;
298 }
299
300 public void setBranding(Map<String, Map<String, String>> branding) {
301 this.branding = branding;
302 }
303
304 public void setStyleSheets(Map<String, List<String>> styleSheets) {
305 this.styleSheets = styleSheets;
306 }
307
308 public void setBundleContext(BundleContext bundleContext) {
309 this.bundleContext = bundleContext;
310 }
311
312 public void setResources(List<String> resources) {
313 this.resources = resources;
314 }
315
316 public void setContextName(String contextName) {
317 this.contextName = contextName;
318 }
319
320 class CmsExceptionHandler implements ExceptionHandler {
321
322 @Override
323 public void handleException(Throwable throwable) {
324 // TODO be smarter
325 CmsUtils.getCmsView().exception(throwable);
326 }
327
328 }
329
330 private class CmsEntryPointFactory implements EntryPointFactory {
331 private final CmsUiProvider page;
332 private final Repository repository;
333 private final String workspace;
334 private final Map<String, String> properties;
335
336 public CmsEntryPointFactory(CmsUiProvider page, Repository repository, String workspace,
337 Map<String, String> properties) {
338 this.page = page;
339 this.repository = repository;
340 this.workspace = workspace;
341 this.properties = properties;
342 }
343
344 @Override
345 public EntryPoint create() {
346 SimpleErgonomics entryPoint = new SimpleErgonomics(repository, workspace, jcrBasePath, page, properties) {
347 private static final long serialVersionUID = -637940404865527290L;
348
349 @Override
350 protected void createAdminArea(Composite parent) {
351 Composite adminArea = new Composite(parent, SWT.NONE);
352 adminArea.setLayout(new FillLayout());
353 Button refresh = new Button(adminArea, SWT.PUSH);
354 refresh.setText("Reload App");
355 refresh.addSelectionListener(new SelectionAdapter() {
356 private static final long serialVersionUID = -7671999525536351366L;
357
358 @Override
359 public void widgetSelected(SelectionEvent e) {
360 long timeBeforeReload = 1000;
361 RWT.getClient().getService(JavaScriptExecutor.class).execute(
362 "setTimeout(function() { " + "location.reload();" + "}," + timeBeforeReload + ");");
363 reloadApp();
364 }
365 });
366 }
367 };
368 // entryPoint.setState("");
369 entryPoint.setHeader(header);
370 entryPoint.setHeaderHeight(headerHeight);
371 // CmsSession.current.set(entryPoint);
372 return entryPoint;
373 }
374
375 private void reloadApp() {
376 new Thread("Refresh app") {
377 @Override
378 public void run() {
379 unregister();
380 register();
381 }
382 }.start();
383 }
384 }
385
386 private static ResourceLoader createResourceLoader(final String resourceName) {
387 return new ResourceLoader() {
388 public InputStream getResourceAsStream(String resourceName) throws IOException {
389 return getClass().getClassLoader().getResourceAsStream(resourceName);
390 }
391 };
392 }
393
394 // private static ResourceLoader createUrlResourceLoader(final URL url) {
395 // return new ResourceLoader() {
396 // public InputStream getResourceAsStream(String resourceName)
397 // throws IOException {
398 // return url.openStream();
399 // }
400 // };
401 // }
402
403 /*
404 * TEXTS
405 */
406 private static String DEFAULT_LOADING_BODY = "<div"
407 + " style=\"position: absolute; left: 50%; top: 50%; margin: -32px -32px; width: 64px; height:64px\">"
408 + "<img src=\"./rwt-resources/" + LOADING_IMAGE
409 + "\" width=\"32\" height=\"32\" style=\"margin: 16px 16px\"/>" + "</div>";
410 }