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