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