View Javadoc
1   package org.argeo.cms.ui.script;
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.Enumeration;
8   import java.util.HashMap;
9   import java.util.Hashtable;
10  import java.util.List;
11  import java.util.Map;
12  
13  import javax.jcr.Node;
14  import javax.jcr.Property;
15  import javax.jcr.PropertyIterator;
16  import javax.jcr.PropertyType;
17  import javax.jcr.Repository;
18  import javax.jcr.RepositoryException;
19  import javax.script.ScriptEngine;
20  import javax.servlet.http.HttpServletRequest;
21  import javax.servlet.http.HttpServletResponse;
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.util.BundleResourceLoader;
29  import org.argeo.cms.ui.util.CmsUiUtils;
30  import org.argeo.cms.ui.util.SimpleErgonomics;
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.ExceptionHandler;
35  import org.eclipse.rap.rwt.client.WebClient;
36  import org.eclipse.rap.rwt.service.ResourceLoader;
37  import org.osgi.framework.Bundle;
38  import org.osgi.framework.BundleContext;
39  import org.osgi.framework.ServiceRegistration;
40  import org.osgi.service.http.HttpContext;
41  import org.osgi.service.http.HttpService;
42  import org.osgi.service.http.NamespaceException;
43  
44  public class CmsScriptApp implements Branding {
45  	public final static String CONTEXT_NAME = "contextName";
46  
47  	ServiceRegistration<ApplicationConfiguration> appConfigReg;
48  
49  	private ScriptEngine scriptEngine;
50  
51  	private final static Log log = LogFactory.getLog(CmsScriptApp.class);
52  
53  	private String webPath;
54  	private String repo = "(cn=node)";
55  
56  	// private Branding branding = new Branding();
57  	private Theme theme;
58  
59  	private List<String> resources = new ArrayList<>();
60  
61  	private Map<String, AppUi> ui = new HashMap<>();
62  
63  	private CmsUiProvider header;
64  	private Integer headerHeight = null;
65  	private CmsUiProvider lead;
66  	private CmsUiProvider end;
67  	private CmsUiProvider footer;
68  
69  	// Branding
70  	private String themeId;
71  	private String additionalHeaders;
72  	private String bodyHtml;
73  	private String pageTitle;
74  	private String pageOverflow;
75  	private String favicon;
76  
77  	public CmsScriptApp(ScriptEngine scriptEngine) {
78  		super();
79  		this.scriptEngine = scriptEngine;
80  	}
81  
82  	public void apply(BundleContext bundleContext, Repository repository, Application application) {
83  		BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle());
84  
85  		application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
86  		// application.setOperationMode(OperationMode.JEE_COMPATIBILITY);
87  
88  		application.setExceptionHandler(new CmsExceptionHandler());
89  
90  		// loading animated gif
91  		application.addResource(CmsConstants.LOADING_IMAGE, createResourceLoader(CmsConstants.LOADING_IMAGE));
92  		// empty image
93  		application.addResource(CmsConstants.NO_IMAGE, createResourceLoader(CmsConstants.NO_IMAGE));
94  
95  		for (String resource : resources) {
96  			application.addResource(resource, bundleRL);
97  			if (log.isTraceEnabled())
98  				log.trace("Resource " + resource);
99  		}
100 
101 		if (theme != null) {
102 			theme.apply(application);
103 			String themeHeaders = theme.getAdditionalHeaders();
104 			if (themeHeaders != null) {
105 				if (additionalHeaders == null)
106 					additionalHeaders = themeHeaders;
107 				else
108 					additionalHeaders = themeHeaders + "\n" + additionalHeaders;
109 			}
110 			themeId = theme.getThemeId();
111 		}
112 
113 		// client JavaScript
114 		Bundle appBundle = bundleRL.getBundle();
115 		BundleContext bc = appBundle.getBundleContext();
116 		HttpService httpService = bc.getService(bc.getServiceReference(HttpService.class));
117 		HttpContext httpContext = new BundleHttpContext(bc);
118 		Enumeration<URL> themeResources = appBundle.findEntries("/js/", "*", true);
119 		if (themeResources != null)
120 			bundleResources: while (themeResources.hasMoreElements()) {
121 				try {
122 					String name = themeResources.nextElement().getPath();
123 					if (name.endsWith("/"))
124 						continue bundleResources;
125 					String alias = "/" + getWebPath() + name;
126 
127 					httpService.registerResources(alias, name, httpContext);
128 					if (log.isDebugEnabled())
129 						log.debug("Mapped " + name + " to alias " + alias);
130 
131 				} catch (NamespaceException e) {
132 					// TODO Auto-generated catch block
133 					e.printStackTrace();
134 				}
135 			}
136 
137 		// App UIs
138 		for (String appUiName : ui.keySet()) {
139 			AppUi appUi = ui.get(appUiName);
140 			appUi.apply(repository, application, this, appUiName);
141 
142 		}
143 
144 	}
145 
146 	public void applySides(SimpleErgonomics simpleErgonomics) {
147 		simpleErgonomics.setHeader(header);
148 		simpleErgonomics.setLead(lead);
149 		simpleErgonomics.setEnd(end);
150 		simpleErgonomics.setFooter(footer);
151 	}
152 
153 	public void register(BundleContext bundleContext, ApplicationConfiguration appConfig) {
154 		Hashtable<String, String> props = new Hashtable<>();
155 		props.put(CONTEXT_NAME, webPath);
156 		appConfigReg = bundleContext.registerService(ApplicationConfiguration.class, appConfig, props);
157 	}
158 
159 	public void reload() {
160 		BundleContext bundleContext = appConfigReg.getReference().getBundle().getBundleContext();
161 		ApplicationConfiguration appConfig = bundleContext.getService(appConfigReg.getReference());
162 		appConfigReg.unregister();
163 		register(bundleContext, appConfig);
164 
165 		// BundleContext bundleContext = (BundleContext)
166 		// getScriptEngine().get("bundleContext");
167 		// try {
168 		// Bundle bundle = bundleContext.getBundle();
169 		// bundle.stop();
170 		// bundle.start();
171 		// } catch (BundleException e) {
172 		// // TODO Auto-generated catch block
173 		// e.printStackTrace();
174 		// }
175 	}
176 
177 	private static ResourceLoader createResourceLoader(final String resourceName) {
178 		return new ResourceLoader() {
179 			public InputStream getResourceAsStream(String resourceName) throws IOException {
180 				return getClass().getClassLoader().getResourceAsStream(resourceName);
181 			}
182 		};
183 	}
184 
185 	public List<String> getResources() {
186 		return resources;
187 	}
188 
189 	public AppUi newUi(String name) {
190 		if (ui.containsKey(name))
191 			throw new IllegalArgumentException("There is already an UI named " + name);
192 		AppUi appUi = new AppUi(this);
193 		// appUi.setApp(this);
194 		ui.put(name, appUi);
195 		return appUi;
196 	}
197 
198 	public void addUi(String name, AppUi appUi) {
199 		if (ui.containsKey(name))
200 			throw new IllegalArgumentException("There is already an UI named " + name);
201 		// appUi.setApp(this);
202 		ui.put(name, appUi);
203 	}
204 
205 	public void applyBranding(Map<String, String> properties) {
206 		if (themeId != null)
207 			properties.put(WebClient.THEME_ID, themeId);
208 		if (additionalHeaders != null)
209 			properties.put(WebClient.HEAD_HTML, additionalHeaders);
210 		if (bodyHtml != null)
211 			properties.put(WebClient.BODY_HTML, bodyHtml);
212 		if (pageTitle != null)
213 			properties.put(WebClient.PAGE_TITLE, pageTitle);
214 		if (pageOverflow != null)
215 			properties.put(WebClient.PAGE_OVERFLOW, pageOverflow);
216 		if (favicon != null)
217 			properties.put(WebClient.FAVICON, favicon);
218 	}
219 
220 	class CmsExceptionHandler implements ExceptionHandler {
221 
222 		@Override
223 		public void handleException(Throwable throwable) {
224 			// TODO be smarter
225 			CmsUiUtils.getCmsView().exception(throwable);
226 		}
227 
228 	}
229 
230 	// public Branding getBranding() {
231 	// return branding;
232 	// }
233 
234 	ScriptEngine getScriptEngine() {
235 		return scriptEngine;
236 	}
237 
238 	public static String toJson(Node node) {
239 		try {
240 			StringBuilder sb = new StringBuilder();
241 			sb.append('{');
242 			PropertyIterator pit = node.getProperties();
243 			int count = 0;
244 			while (pit.hasNext()) {
245 				Property p = pit.nextProperty();
246 				int type = p.getType();
247 				if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE || type == PropertyType.PATH) {
248 					Node ref = p.getNode();
249 					if (count != 0)
250 						sb.append(',');
251 					// TODO limit depth?
252 					sb.append(toJson(ref));
253 					count++;
254 				} else if (!p.isMultiple()) {
255 					if (count != 0)
256 						sb.append(',');
257 					sb.append('\"').append(p.getName()).append("\":\"").append(p.getString()).append('\"');
258 					count++;
259 				}
260 			}
261 			sb.append('}');
262 			return sb.toString();
263 		} catch (RepositoryException e) {
264 			throw new CmsException("Cannot convert " + node + " to JSON", e);
265 		}
266 	}
267 
268 	public void fromJson(Node node, String json) {
269 		// TODO
270 	}
271 
272 	public Theme getTheme() {
273 		return theme;
274 	}
275 
276 	public void setTheme(Theme theme) {
277 		this.theme = theme;
278 	}
279 
280 	public String getWebPath() {
281 		return webPath;
282 	}
283 
284 	public void setWebPath(String context) {
285 		this.webPath = context;
286 	}
287 
288 	public String getRepo() {
289 		return repo;
290 	}
291 
292 	public void setRepo(String repo) {
293 		this.repo = repo;
294 	}
295 
296 	public Map<String, AppUi> getUi() {
297 		return ui;
298 	}
299 
300 	public void setUi(Map<String, AppUi> ui) {
301 		this.ui = ui;
302 	}
303 
304 	// Branding
305 	public String getThemeId() {
306 		return themeId;
307 	}
308 
309 	public void setThemeId(String themeId) {
310 		this.themeId = themeId;
311 	}
312 
313 	public String getAdditionalHeaders() {
314 		return additionalHeaders;
315 	}
316 
317 	public void setAdditionalHeaders(String additionalHeaders) {
318 		this.additionalHeaders = additionalHeaders;
319 	}
320 
321 	public String getBodyHtml() {
322 		return bodyHtml;
323 	}
324 
325 	public void setBodyHtml(String bodyHtml) {
326 		this.bodyHtml = bodyHtml;
327 	}
328 
329 	public String getPageTitle() {
330 		return pageTitle;
331 	}
332 
333 	public void setPageTitle(String pageTitle) {
334 		this.pageTitle = pageTitle;
335 	}
336 
337 	public String getPageOverflow() {
338 		return pageOverflow;
339 	}
340 
341 	public void setPageOverflow(String pageOverflow) {
342 		this.pageOverflow = pageOverflow;
343 	}
344 
345 	public String getFavicon() {
346 		return favicon;
347 	}
348 
349 	public void setFavicon(String favicon) {
350 		this.favicon = favicon;
351 	}
352 
353 	public CmsUiProvider getHeader() {
354 		return header;
355 	}
356 
357 	public void setHeader(CmsUiProvider header) {
358 		this.header = header;
359 	}
360 
361 	public Integer getHeaderHeight() {
362 		return headerHeight;
363 	}
364 
365 	public void setHeaderHeight(Integer headerHeight) {
366 		this.headerHeight = headerHeight;
367 	}
368 
369 	public CmsUiProvider getLead() {
370 		return lead;
371 	}
372 
373 	public void setLead(CmsUiProvider lead) {
374 		this.lead = lead;
375 	}
376 
377 	public CmsUiProvider getEnd() {
378 		return end;
379 	}
380 
381 	public void setEnd(CmsUiProvider end) {
382 		this.end = end;
383 	}
384 
385 	public CmsUiProvider getFooter() {
386 		return footer;
387 	}
388 
389 	public void setFooter(CmsUiProvider footer) {
390 		this.footer = footer;
391 	}
392 
393 	static class BundleHttpContext implements HttpContext {
394 		private BundleContext bundleContext;
395 
396 		public BundleHttpContext(BundleContext bundleContext) {
397 			super();
398 			this.bundleContext = bundleContext;
399 		}
400 
401 		@Override
402 		public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException {
403 			// TODO Auto-generated method stub
404 			return true;
405 		}
406 
407 		@Override
408 		public URL getResource(String name) {
409 
410 			return bundleContext.getBundle().getEntry(name);
411 		}
412 
413 		@Override
414 		public String getMimeType(String name) {
415 			return null;
416 		}
417 
418 	}
419 
420 }