1 | /** |
2 | * Copyright 2005-2011 Steve McDuff d-duff@users.sourceforge.net |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | package org.deduced.viewer.web.server; |
17 | |
18 | import java.io.File; |
19 | import java.io.FileInputStream; |
20 | import java.io.Serializable; |
21 | import java.security.AccessController; |
22 | import java.security.PrivilegedAction; |
23 | import java.util.ArrayList; |
24 | import java.util.List; |
25 | import java.util.Map.Entry; |
26 | import java.util.Properties; |
27 | import java.util.Set; |
28 | import java.util.logging.Level; |
29 | import java.util.logging.Logger; |
30 | |
31 | import javax.servlet.ServletContext; |
32 | import javax.servlet.ServletException; |
33 | |
34 | import org.apache.commons.io.IOUtils; |
35 | import org.apache.commons.lang.ObjectUtils; |
36 | import org.deduced.DeducedModelLayerExtension; |
37 | import org.deduced.DeducedUtilities; |
38 | import org.deduced.ModelFactory; |
39 | import org.deduced.Property; |
40 | import org.deduced.PropertyCollection; |
41 | import org.deduced.dynamic.AbstractDynamicExecutionCompiler; |
42 | import org.deduced.dynamic.AbstractDynamicExecutionCompiler.LocalClassLoader; |
43 | import org.deduced.framework.ControllerSchema; |
44 | import org.deduced.framework.FrameworkLoader; |
45 | import org.deduced.implementation.PropertyInstanceImplementation; |
46 | import org.deduced.live.StartLiveApplication; |
47 | import org.deduced.rule.DeductionRuleLibrary; |
48 | import org.deduced.utilities.AssertUtilities; |
49 | import org.deduced.utilities.ExceptionRunnable; |
50 | import org.deduced.utilities.FileUtilities; |
51 | import org.deduced.utilities.ObjectUtilities; |
52 | import org.deduced.utilities.Runner; |
53 | import org.deduced.utilities.SwingInvokeAndWaitRunner; |
54 | import org.deduced.validation.ValidationUtilities; |
55 | import org.deduced.viewer.framework.DeducedViewLayerExtensionImplementation; |
56 | import org.deduced.viewer.framework.ViewSchema; |
57 | import org.deduced.viewer.web.serializer.MasterWebSerializer; |
58 | import org.deduced.viewer.web.serializer.MasterWebSerializerImplementation; |
59 | import org.deduced.viewer.web.shared.ChangeEvent; |
60 | import org.deduced.viewer.web.shared.DynamicUserInterfaceService; |
61 | import org.deduced.viewer.web.shared.SerializedValue; |
62 | import org.deduced.viewer.web.shared.ViewModel; |
63 | |
64 | import com.google.gwt.user.server.rpc.RemoteServiceServlet; |
65 | |
66 | /** |
67 | * DynamicUserInterfaceServiceImplementation is the main remote service servlet |
68 | * that will start the deduced application and offer the services used to |
69 | * interact with a client. |
70 | * |
71 | * @author Steve McDuff |
72 | * |
73 | */ |
74 | public class DynamicUserInterfaceServiceImplementation extends |
75 | RemoteServiceServlet implements DynamicUserInterfaceService |
76 | { |
77 | |
78 | /** |
79 | * serialVersionUID |
80 | */ |
81 | private static final long serialVersionUID = -3456009224256315787L; |
82 | |
83 | /** |
84 | * logger |
85 | */ |
86 | private static final Logger LOGGER = Logger |
87 | .getLogger(DynamicUserInterfaceServiceImplementation.class.getName()); |
88 | |
89 | /** |
90 | * application file property name |
91 | */ |
92 | public static final String DEDUCED_APPLICATION_FILE = |
93 | "deduced.application.file"; |
94 | |
95 | /** |
96 | * dynamic code generation folder property name |
97 | */ |
98 | public static final String DEDUCED_DYNAMIC_CODE_GENERATION_FOLDER = |
99 | "deduced.dynamic.code.generation.folder"; |
100 | |
101 | /** |
102 | * dynamic code library folder property name |
103 | */ |
104 | public static final String DEDUCED_DYNAMIC_CODE_LIBRARY_FOLDER = |
105 | "deduced.dynamic.code.library.folder"; |
106 | |
107 | /** |
108 | * name of the web view object |
109 | */ |
110 | public static final String DEDUCED_WEB_VIEW_NAME = "deduced.web.view.name"; |
111 | |
112 | /** |
113 | * deduced live application |
114 | */ |
115 | private StartLiveApplication application; |
116 | |
117 | /** |
118 | * user interface root collection |
119 | */ |
120 | private PropertyCollection uiRoot; |
121 | |
122 | /** |
123 | * deduced application request runner to ensure that requests are done in |
124 | * the right thread. |
125 | */ |
126 | private Runner requestRunner = new SwingInvokeAndWaitRunner(); |
127 | |
128 | /** |
129 | * property collection index that references all UI collections and monitors |
130 | * their change events. |
131 | */ |
132 | private PropertyCollectionIndex listener = new PropertyCollectionIndex(); |
133 | |
134 | /** |
135 | * change event aggregator to accumulate change events and send them to the |
136 | * client |
137 | */ |
138 | private SerializedChangeEventAggregator aggregator = |
139 | new SerializedChangeEventAggregator(); |
140 | |
141 | /** |
142 | * implementation of the serializer used to send the final version to the |
143 | * client |
144 | */ |
145 | private MasterWebSerializer serializer = null; |
146 | |
147 | /** |
148 | * web view layer |
149 | */ |
150 | private DeducedModelLayerExtension viewLayer; |
151 | |
152 | /** |
153 | * file to open when starting the application |
154 | */ |
155 | private String openFileName = null; |
156 | |
157 | /** |
158 | * folder name where the dynamic code is generated |
159 | */ |
160 | private String dynamicCodeGenerationFolder = null; |
161 | |
162 | /** |
163 | * folder name where the dynamic code libraries are located |
164 | */ |
165 | private String dynamicCodeLibraryFolder = null; |
166 | |
167 | /** |
168 | * web view object name |
169 | */ |
170 | private String webViewObjectName = null; |
171 | |
172 | /** |
173 | * |
174 | * DynamicUserInterfaceServiceImplementation constructor |
175 | */ |
176 | public DynamicUserInterfaceServiceImplementation() |
177 | { |
178 | |
179 | } |
180 | |
181 | /** |
182 | * (non-JSDoc) |
183 | * |
184 | * @see javax.servlet.GenericServlet#init() |
185 | */ |
186 | @Override |
187 | public void init() throws ServletException |
188 | { |
189 | super.init(); |
190 | |
191 | initialize(); |
192 | } |
193 | |
194 | /** |
195 | * initialize |
196 | */ |
197 | protected void initialize() |
198 | { |
199 | loadConfiguration(); |
200 | |
201 | configureTemporaryRuleFiles(); |
202 | |
203 | configureRuleExecutionClassLoader(); |
204 | |
205 | configureCompilerArguments(); |
206 | |
207 | configureDynamicCodeOutputFolderName(); |
208 | |
209 | initializeDeducedApplication(); |
210 | |
211 | initializeInfrastructure(); |
212 | } |
213 | |
214 | /** |
215 | * set all Configured Values |
216 | * |
217 | * @param deducedProperties the properties used to start the application |
218 | */ |
219 | private void setConfiguredValues( |
220 | Properties deducedProperties) |
221 | { |
222 | String configuredOpenFileName = |
223 | (String) deducedProperties.get(DEDUCED_APPLICATION_FILE); |
224 | |
225 | configureOpenFileName(configuredOpenFileName); |
226 | |
227 | String configuredDynamicCodeGenerationFolder = |
228 | (String) deducedProperties |
229 | .get(DEDUCED_DYNAMIC_CODE_GENERATION_FOLDER); |
230 | |
231 | configureDynamicCodeGenerationFolder(configuredDynamicCodeGenerationFolder); |
232 | |
233 | String configuredDynamicCodeLibraryFolder = |
234 | (String) deducedProperties.get(DEDUCED_DYNAMIC_CODE_LIBRARY_FOLDER); |
235 | |
236 | configureDynamicCodeLibraryFolder(configuredDynamicCodeLibraryFolder); |
237 | |
238 | String configuredWebViewName = |
239 | (String) deducedProperties.get(DEDUCED_WEB_VIEW_NAME); |
240 | |
241 | configureWebViewName(configuredWebViewName); |
242 | } |
243 | |
244 | /** |
245 | * configure Web View Name |
246 | * |
247 | * @param configuredWebViewName the configured value |
248 | */ |
249 | private void configureWebViewName( |
250 | String configuredWebViewName) |
251 | { |
252 | String finalName = configuredWebViewName; |
253 | if (configuredWebViewName == null) |
254 | { |
255 | finalName = "web view"; |
256 | |
257 | LOGGER.log(Level.INFO, "No value specified in \"" |
258 | + DEDUCED_WEB_VIEW_NAME + "\". Using default value of : \"" |
259 | + finalName + "\"."); |
260 | |
261 | } |
262 | else |
263 | { |
264 | LOGGER.log(Level.INFO, "Configured value \"" |
265 | + DEDUCED_WEB_VIEW_NAME + "\" set to \"" + finalName + "\"."); |
266 | } |
267 | webViewObjectName = finalName; |
268 | } |
269 | |
270 | /** |
271 | * configure Dynamic Code Generation Folder |
272 | * |
273 | * @param configuredDynamicCodeGenerationFolder the configured value |
274 | */ |
275 | private void configureDynamicCodeGenerationFolder( |
276 | String configuredDynamicCodeGenerationFolder) |
277 | { |
278 | String pathWithDefault = configuredDynamicCodeGenerationFolder; |
279 | if (pathWithDefault == null) |
280 | { |
281 | pathWithDefault = "WEB-INF/deduced/generated"; |
282 | } |
283 | |
284 | String resolvedPath = |
285 | resolvePathRelativeToFileSystemOrWar(pathWithDefault); |
286 | |
287 | dynamicCodeGenerationFolder = resolvedPath; |
288 | |
289 | if (configuredDynamicCodeGenerationFolder == null) |
290 | { |
291 | LOGGER.log(Level.INFO, "No value specified in \"" |
292 | + DEDUCED_DYNAMIC_CODE_GENERATION_FOLDER |
293 | + "\". Using default value of \"" + pathWithDefault |
294 | + "\". Generating dynamic code in folder \"" + resolvedPath |
295 | + "\"."); |
296 | } |
297 | else |
298 | { |
299 | LOGGER.log(Level.INFO, "Configured value \"" |
300 | + DEDUCED_DYNAMIC_CODE_GENERATION_FOLDER + "\" set to \"" |
301 | + configuredDynamicCodeGenerationFolder |
302 | + "\". Generating dynamic code in folder \"" + resolvedPath |
303 | + "\"."); |
304 | } |
305 | } |
306 | |
307 | /** |
308 | * configure Dynamic Code Library Folder |
309 | * |
310 | * @param configuredDynamicCodeLibraryFolder the configured value |
311 | */ |
312 | private void configureDynamicCodeLibraryFolder( |
313 | String configuredDynamicCodeLibraryFolder) |
314 | { |
315 | String pathWithDefault = configuredDynamicCodeLibraryFolder; |
316 | if (pathWithDefault == null) |
317 | { |
318 | pathWithDefault = "WEB-INF/lib"; |
319 | } |
320 | |
321 | String resolvedPath = |
322 | resolvePathRelativeToFileSystemOrWar(pathWithDefault); |
323 | |
324 | dynamicCodeLibraryFolder = resolvedPath; |
325 | |
326 | if (configuredDynamicCodeLibraryFolder == null) |
327 | { |
328 | LOGGER.log(Level.INFO, "No value specified in \"" |
329 | + DEDUCED_DYNAMIC_CODE_LIBRARY_FOLDER |
330 | + "\". Using default value of \"" + pathWithDefault |
331 | + "\". Dynamic code library folder resolved to \"" |
332 | + resolvedPath + "\"."); |
333 | } |
334 | else |
335 | { |
336 | LOGGER.log(Level.INFO, "Configured value \"" |
337 | + DEDUCED_DYNAMIC_CODE_LIBRARY_FOLDER + "\" set to \"" |
338 | + configuredDynamicCodeLibraryFolder |
339 | + "\". Dynamic code library folder resolved to \"" |
340 | + resolvedPath + "\"."); |
341 | } |
342 | } |
343 | |
344 | /** |
345 | * configure the Open File Name by looking on the system and on the WAR for |
346 | * the specified name |
347 | * |
348 | * @param configuredOpenFileName the configured file name |
349 | */ |
350 | private void configureOpenFileName( |
351 | String configuredOpenFileName) |
352 | { |
353 | String resolvedPath = |
354 | resolvePathRelativeToFileSystemOrWar(configuredOpenFileName); |
355 | |
356 | setOpenFileName(resolvedPath); |
357 | |
358 | if (resolvedPath == null) |
359 | { |
360 | LOGGER.log(Level.INFO, "No file specified in \"" |
361 | + DEDUCED_APPLICATION_FILE + "\". Starting a new application."); |
362 | } |
363 | else |
364 | { |
365 | LOGGER.log(Level.INFO, "Configured value \"" |
366 | + DEDUCED_APPLICATION_FILE + "\" set to \"" |
367 | + configuredOpenFileName |
368 | + "\". Initializing the application using \"" + resolvedPath |
369 | + "\"."); |
370 | } |
371 | } |
372 | |
373 | /** |
374 | * resolve a Path Relative To the File System Or War file folder. If the |
375 | * configured path starts with the "WEB-INF" prefix, then a relative path to |
376 | * the WAR file is used. |
377 | * |
378 | * @param configuredPath the configured path |
379 | * @return the resolved system file path |
380 | */ |
381 | public String resolvePathRelativeToFileSystemOrWar( |
382 | String configuredPath) |
383 | { |
384 | String resolvedPath = null; |
385 | if (configuredPath != null) |
386 | { |
387 | if (configuredPath.toLowerCase().startsWith("web-inf")) |
388 | { |
389 | // file not found on file system, reverting to WAR relative path |
390 | ServletContext servletContext = getServletContext(); |
391 | |
392 | resolvedPath = servletContext.getRealPath(configuredPath); |
393 | } |
394 | else |
395 | { |
396 | resolvedPath = configuredPath; |
397 | } |
398 | } |
399 | return resolvedPath; |
400 | } |
401 | |
402 | /** |
403 | * load Configuration |
404 | */ |
405 | private void loadConfiguration() |
406 | { |
407 | ServletContext servletContext = getServletContext(); |
408 | |
409 | Properties deducedProperties = new Properties(); |
410 | |
411 | String deducedPropertiesFilePath = |
412 | servletContext.getRealPath("WEB-INF/deduced/deduced.properties"); |
413 | |
414 | FileInputStream fis = null; |
415 | try |
416 | { |
417 | fis = new FileInputStream(deducedPropertiesFilePath); |
418 | deducedProperties.load(fis); |
419 | } |
420 | catch (Exception e) |
421 | { |
422 | LOGGER.log(Level.WARNING, |
423 | "Failed to load the deduced web application configuration files." |
424 | + " Using default values.", e); |
425 | } |
426 | finally |
427 | { |
428 | IOUtils.closeQuietly(fis); |
429 | } |
430 | |
431 | setConfiguredValues(deducedProperties); |
432 | } |
433 | |
434 | /** |
435 | * configure Dynamic Code Output Folder Name to use the WEB-INF folder to |
436 | * ensure we don't expose generated code |
437 | */ |
438 | private void configureDynamicCodeOutputFolderName() |
439 | { |
440 | AbstractDynamicExecutionCompiler |
441 | .setOutputFolderName(dynamicCodeGenerationFolder); |
442 | } |
443 | |
444 | /** |
445 | * |
446 | * initializeInfrastructure |
447 | */ |
448 | protected void initializeInfrastructure() |
449 | { |
450 | serializer = new MasterWebSerializerImplementation(); |
451 | aggregator.setSerializer(serializer); |
452 | listener.setLinkedListener(aggregator); |
453 | } |
454 | |
455 | /** |
456 | * get Open File Name |
457 | * |
458 | * @return the open file name |
459 | */ |
460 | public String getOpenFileName() |
461 | { |
462 | return openFileName; |
463 | } |
464 | |
465 | /** |
466 | * set Open File Name |
467 | * |
468 | * @param newOpenFileName the file name to open |
469 | */ |
470 | public void setOpenFileName( |
471 | String newOpenFileName) |
472 | { |
473 | openFileName = newOpenFileName; |
474 | } |
475 | |
476 | /** |
477 | * configure the Rule Execution Class Loader to use right web application |
478 | * class loader |
479 | */ |
480 | private void configureRuleExecutionClassLoader() |
481 | { |
482 | LocalClassLoader newLoader = |
483 | AccessController |
484 | .doPrivileged(new PrivilegedAction<LocalClassLoader>() |
485 | { |
486 | @Override |
487 | public LocalClassLoader run() |
488 | { |
489 | return new LocalClassLoader(Thread.currentThread() |
490 | .getContextClassLoader()); |
491 | } |
492 | }); |
493 | AbstractDynamicExecutionCompiler.setDynamicClassLoader(newLoader); |
494 | } |
495 | |
496 | /** |
497 | * configure the Compiler Arguments to be passed during dynamic code |
498 | * compilation so that we may find the required JARs. |
499 | */ |
500 | private void configureCompilerArguments() |
501 | { |
502 | List<String> argumentList = new ArrayList<String>(); |
503 | |
504 | boolean isDeducedFoundInSystemClassLoader = |
505 | isDeducedFoundInSystemClassLoader(); |
506 | |
507 | if (isDeducedFoundInSystemClassLoader) |
508 | { |
509 | LOGGER.log(Level.INFO, |
510 | "The deduced framework classes are present in the system classpath." |
511 | + " Ignoring the configured library folder."); |
512 | } |
513 | else |
514 | { |
515 | String compilationJarList = |
516 | getJarListFromFolder(dynamicCodeLibraryFolder); |
517 | |
518 | argumentList.add("-cp"); |
519 | argumentList.add(compilationJarList); |
520 | |
521 | LOGGER.log(Level.INFO, |
522 | "The dynamic code compiler will use the following" |
523 | + " path to find the deduced framework classes : " |
524 | + compilationJarList); |
525 | } |
526 | |
527 | if (argumentList.size() > 0) |
528 | { |
529 | String[] argumentArray = |
530 | argumentList.toArray(new String[argumentList.size()]); |
531 | AbstractDynamicExecutionCompiler |
532 | .setCompilerArguments(argumentArray); |
533 | } |
534 | } |
535 | |
536 | /** |
537 | * get Jar List From Folder and return it as a classpath |
538 | * |
539 | * @param libraryFolderName the folder name to scan |
540 | * @return the list of jars in a comma separated list. |
541 | */ |
542 | private String getJarListFromFolder( |
543 | String libraryFolderName) |
544 | { |
545 | File[] jarListFromFolder = |
546 | FileUtilities.getJarListFromFolder(libraryFolderName); |
547 | |
548 | List<String> canonicalFilePathList = |
549 | FileUtilities.getCanonicalFilePathList(jarListFromFolder); |
550 | |
551 | String returnValue = |
552 | ObjectUtilities.aggregateStringListWithSeparator( |
553 | canonicalFilePathList, File.pathSeparator); |
554 | |
555 | return returnValue; |
556 | } |
557 | |
558 | /** |
559 | * check if Deduced classes are Found In the System Class Loader |
560 | * |
561 | * @return true if the system class loader has access to the deduced |
562 | * libraries |
563 | */ |
564 | protected boolean isDeducedFoundInSystemClassLoader() |
565 | { |
566 | boolean isDeducedFoundInSystemClassLoader = false; |
567 | |
568 | try |
569 | { |
570 | ClassLoader.getSystemClassLoader().loadClass( |
571 | DeductionRuleLibrary.class.getName()); |
572 | isDeducedFoundInSystemClassLoader = true; |
573 | |
574 | LOGGER |
575 | .log(Level.FINE, |
576 | "The deduced framework classes were found in the system class loader."); |
577 | } |
578 | catch (ClassNotFoundException e) |
579 | { |
580 | LOGGER |
581 | .log(Level.FINE, |
582 | "The deduced framework classes weren't found in the system class loader."); |
583 | } |
584 | return isDeducedFoundInSystemClassLoader; |
585 | } |
586 | |
587 | /** |
588 | * initialize Deduced Application |
589 | */ |
590 | protected void initializeDeducedApplication() |
591 | { |
592 | requestRunner.runSilent(new Runnable() |
593 | { |
594 | @Override |
595 | public void run() |
596 | { |
597 | FrameworkLoader.createBaseSchema(); |
598 | ViewSchema.initializeFromXml(); |
599 | ControllerSchema.initializeFromXml(); |
600 | |
601 | StartLiveApplication newApplication = |
602 | new StartLiveApplication(null); |
603 | setApplication(newApplication); |
604 | |
605 | String currentOpenFileName = getOpenFileName(); |
606 | newApplication.startUserApplication(currentOpenFileName); |
607 | newApplication.startDevelopmentApplication(); |
608 | |
609 | createWebViewLayer(); |
610 | } |
611 | }); |
612 | } |
613 | |
614 | /** |
615 | * set Application |
616 | * |
617 | * @param newApplication the new application |
618 | */ |
619 | protected void setApplication( |
620 | StartLiveApplication newApplication) |
621 | { |
622 | application = newApplication; |
623 | } |
624 | |
625 | /** |
626 | * stop the user application |
627 | */ |
628 | public void stop() |
629 | { |
630 | if (application != null) |
631 | { |
632 | application.getDevelopmentApplication().stop(); |
633 | application.getUserApplication().stop(); |
634 | } |
635 | } |
636 | |
637 | /** |
638 | * get the live application |
639 | * |
640 | * @return the live application. |
641 | */ |
642 | public StartLiveApplication getApplication() |
643 | { |
644 | return application; |
645 | } |
646 | |
647 | /** |
648 | * create the Web View Layer |
649 | */ |
650 | protected void createWebViewLayer() |
651 | { |
652 | DeducedViewLayerExtensionImplementation applicationView = |
653 | application.getUserApplication().getApplicationView(); |
654 | |
655 | viewLayer = applicationView; |
656 | |
657 | PropertyCollection<?, ?> webView = |
658 | DeducedUtilities.findChildByName( |
659 | applicationView.getCollectionList(), webViewObjectName); |
660 | |
661 | if (webView == null) |
662 | { |
663 | LOGGER |
664 | .log( |
665 | Level.SEVERE, |
666 | "Failed to find the web view named \"" |
667 | + webViewObjectName |
668 | + "\" located under the view layer. No web view will be available."); |
669 | } |
670 | else |
671 | { |
672 | setUiRoot(webView); |
673 | } |
674 | } |
675 | |
676 | /** |
677 | * set the user interface root |
678 | * |
679 | * @param webView the root view |
680 | */ |
681 | public void setUiRoot( |
682 | PropertyCollection<?, ?> webView) |
683 | { |
684 | if (webView != null) |
685 | { |
686 | uiRoot = webView; |
687 | listener.setModel(uiRoot); |
688 | } |
689 | } |
690 | |
691 | /** |
692 | * get Property Collection Index |
693 | * |
694 | * @return the property collection index |
695 | */ |
696 | public PropertyCollectionIndex getPropertyCollectionIndex() |
697 | { |
698 | return listener; |
699 | } |
700 | |
701 | /** |
702 | * configure Temporary Rule Files |
703 | */ |
704 | public void configureTemporaryRuleFiles() |
705 | { |
706 | AbstractDynamicExecutionCompiler |
707 | .setAutomaticallyDeletingGeneratedJavaFiles(false); |
708 | AbstractDynamicExecutionCompiler |
709 | .setAutomaticallyDeletingGeneratedClassFiles(false); |
710 | AbstractDynamicExecutionCompiler |
711 | .setAutomaticallyDeletingGeneratedJavaFilesIfCompileFails(false); |
712 | } |
713 | |
714 | /** |
715 | * (non-JSDoc) |
716 | * |
717 | * @see org.deduced.viewer.web.shared.DynamicUserInterfaceService#getInitialUserInterface() |
718 | */ |
719 | @Override |
720 | public ViewModel getInitialUserInterface() throws Exception |
721 | { |
722 | if (LOGGER.isLoggable(Level.FINE)) |
723 | { |
724 | LOGGER.log(Level.FINE, "getInitialUserInterface entry."); |
725 | } |
726 | |
727 | ViewModel serializeModel = null; |
728 | try |
729 | { |
730 | serializeModel = (ViewModel) serializer.serialize(uiRoot); |
731 | aggregator.getChangeEventList().clear(); |
732 | } |
733 | catch (Exception e) |
734 | { |
735 | if (LOGGER.isLoggable(Level.SEVERE)) |
736 | { |
737 | LOGGER.log(Level.SEVERE, |
738 | "getInitialUserInterface exit failed : " + e.getMessage(), |
739 | e); |
740 | } |
741 | |
742 | throw convertToSerializableException(e); |
743 | } |
744 | |
745 | if (LOGGER.isLoggable(Level.FINE)) |
746 | { |
747 | LOGGER.log(Level.FINE, "getInitialUserInterface exit success."); |
748 | } |
749 | |
750 | return serializeModel; |
751 | } |
752 | |
753 | /** |
754 | * (non-JSDoc) |
755 | * |
756 | * @see org.deduced.viewer.web.shared.DynamicUserInterfaceService#getChangeEventList() |
757 | */ |
758 | @Override |
759 | public ArrayList<ChangeEvent> getChangeEventList() throws Exception |
760 | { |
761 | if (LOGGER.isLoggable(Level.FINEST)) |
762 | { |
763 | LOGGER.log(Level.FINEST, "getChangeEventList entry."); |
764 | } |
765 | |
766 | ArrayList<ChangeEvent> retVal = new ArrayList<ChangeEvent>(); |
767 | |
768 | try |
769 | { |
770 | List<ChangeEvent> changeEventList = aggregator.getChangeEventList(); |
771 | |
772 | if (changeEventList.size() > 0) |
773 | { |
774 | ChangeEvent remove = null; |
775 | |
776 | while (changeEventList.size() > 0) |
777 | { |
778 | remove = changeEventList.remove(0); |
779 | retVal.add(remove); |
780 | } |
781 | } |
782 | } |
783 | catch (Exception ex) |
784 | { |
785 | if (LOGGER.isLoggable(Level.SEVERE)) |
786 | { |
787 | LOGGER.log(Level.FINE, |
788 | "getChangeEventList exit failed. " + ex.getMessage(), ex); |
789 | } |
790 | |
791 | throw convertToSerializableException(ex); |
792 | } |
793 | |
794 | if (LOGGER.isLoggable(Level.FINEST)) |
795 | { |
796 | LOGGER.log(Level.FINEST, "getChangeEventList exit. Returned " |
797 | + retVal.size() + " events."); |
798 | } |
799 | |
800 | return retVal; |
801 | } |
802 | |
803 | /** |
804 | * (non-JSDoc) |
805 | * |
806 | * @see org.deduced.viewer.web.shared.DynamicUserInterfaceService#updateProperty(java.lang.String, |
807 | * java.lang.String, org.deduced.viewer.web.shared.SerializedValue) |
808 | */ |
809 | @Override |
810 | public void updateProperty( |
811 | final String objectID, final String propertyName, |
812 | final SerializedValue newValue) throws Exception |
813 | { |
814 | if (LOGGER.isLoggable(Level.FINE)) |
815 | { |
816 | LOGGER.log(Level.FINE, "updateProperty entry : collection ID : \"" |
817 | + objectID + "\" property name : \"" + propertyName |
818 | + "\" new value : \"" + newValue + "\". "); |
819 | } |
820 | |
821 | try |
822 | { |
823 | requestRunner.run(new ExceptionRunnable() |
824 | { |
825 | @Override |
826 | public void runAndThrow() throws Exception |
827 | { |
828 | runUpdateProperty(objectID, propertyName, newValue); |
829 | } |
830 | }); |
831 | } |
832 | catch (Exception e) |
833 | { |
834 | if (LOGGER.isLoggable(Level.FINE)) |
835 | { |
836 | LOGGER.log( |
837 | Level.FINE, |
838 | "updateProperty exit failed : collection ID : \"" |
839 | + objectID + "\" property name : \"" + propertyName |
840 | + "\" new value : \"" + newValue + "\". " |
841 | + e.getMessage(), e); |
842 | } |
843 | throw convertToSerializableException(e); |
844 | } |
845 | |
846 | if (LOGGER.isLoggable(Level.FINE)) |
847 | { |
848 | LOGGER.log(Level.FINE, |
849 | "updateProperty exit success : collection ID : \"" + objectID |
850 | + "\" property name : \"" + propertyName |
851 | + "\" new value : \"" + newValue + "\". "); |
852 | } |
853 | |
854 | } |
855 | |
856 | /** |
857 | * run Update Property |
858 | * |
859 | * @param objectID the object ID to update |
860 | * @param propertyName the name of the property to update |
861 | * @param newValue the new value |
862 | * @throws Exception any error encountered while performing the update |
863 | */ |
864 | @SuppressWarnings("unchecked") |
865 | protected void runUpdateProperty( |
866 | String objectID, String propertyName, SerializedValue newValue) |
867 | throws Exception |
868 | { |
869 | try |
870 | { |
871 | Serializable serializedValue = newValue.getValue(); |
872 | PropertyCollection collection = getCollectionFromStingID(objectID); |
873 | |
874 | if (collection == null) |
875 | { |
876 | throw new IllegalArgumentException( |
877 | "Unable to find collection of ID : \"" + objectID |
878 | + "\", for which to update : \"" + propertyName |
879 | + "\" to value \"" + serializedValue + "\"."); |
880 | } |
881 | Entry<?, ? extends Property<?>> entry = |
882 | getPropertyEntryByName(propertyName, collection); |
883 | |
884 | if (entry == null) |
885 | { |
886 | throw new IllegalArgumentException( |
887 | "Unable to find the property :\"" + propertyName |
888 | + "\" on collection of ID : \"" + objectID |
889 | + "\", named \"" |
890 | + DeducedUtilities.getCollectionName(collection) |
891 | + "\", on which to set value \"" + serializedValue |
892 | + "\"."); |
893 | } |
894 | |
895 | ModelFactory modelFactory = viewLayer.getModelFactory(); |
896 | Property<?> value = entry.getValue(); |
897 | PropertyCollection<?, ?> instance = value.getInstance(); |
898 | |
899 | Object setValue = serializedValue; |
900 | |
901 | if (PropertyInstanceImplementation.isReference(instance) |
902 | .booleanValue()) |
903 | { |
904 | setValue = getCollectionFromStingID((String) serializedValue); |
905 | } |
906 | |
907 | ValidationUtilities.validateNewValueOnInstance(setValue, |
908 | modelFactory, instance, true); |
909 | |
910 | Object key = entry.getKey(); |
911 | collection.setProperty(key, setValue); |
912 | |
913 | } |
914 | catch (Exception e) |
915 | { |
916 | AssertUtilities.exception(e); |
917 | throw e; |
918 | } |
919 | } |
920 | |
921 | /** |
922 | * get a Property Entry By Name |
923 | * |
924 | * @param propertyName name of the property |
925 | * @param collection collection from which to fetch the property entry |
926 | * @return the matching property entry |
927 | */ |
928 | public static Entry<?, ? extends Property<?>> getPropertyEntryByName( |
929 | String propertyName, PropertyCollection<?, ?> collection) |
930 | { |
931 | Entry<?, ? extends Property<?>> retVal = null; |
932 | |
933 | if (collection != null) |
934 | { |
935 | Set<? extends Entry<?, ? extends Property<?>>> asPropertyList = |
936 | collection.asPropertyMap().entrySet(); |
937 | for (Entry<?, ? extends Property<?>> entry : asPropertyList) |
938 | { |
939 | PropertyCollection<?, ?> instance = |
940 | entry.getValue().getInstance(); |
941 | String instanceName = |
942 | DeducedUtilities.getCollectionName(instance); |
943 | if (ObjectUtils.equals(propertyName, instanceName)) |
944 | { |
945 | retVal = entry; |
946 | break; |
947 | } |
948 | } |
949 | } |
950 | return retVal; |
951 | } |
952 | |
953 | /** |
954 | * (non-JSDoc) |
955 | * |
956 | * @see org.deduced.viewer.web.shared.DynamicUserInterfaceService#updateReferenceList(java.lang.String, |
957 | * java.util.ArrayList) |
958 | */ |
959 | @Override |
960 | public void updateReferenceList( |
961 | final String objectID, final ArrayList<String> selectedObjectIDList) |
962 | throws Exception |
963 | { |
964 | if (LOGGER.isLoggable(Level.FINE)) |
965 | { |
966 | LOGGER.log(Level.FINE, |
967 | "updateReferenceList entry : collection ID : \"" + objectID |
968 | + "\" reference list : \"" |
969 | + printStringList(selectedObjectIDList) + "\"."); |
970 | } |
971 | |
972 | try |
973 | { |
974 | requestRunner.run(new ExceptionRunnable() |
975 | { |
976 | @Override |
977 | public void runAndThrow() throws Exception |
978 | { |
979 | runUpdateReferenceList(objectID, selectedObjectIDList); |
980 | } |
981 | }); |
982 | } |
983 | catch (Exception e) |
984 | { |
985 | if (LOGGER.isLoggable(Level.FINE)) |
986 | { |
987 | LOGGER.log( |
988 | Level.FINE, |
989 | "updateReferenceList exit failed : collection ID : \"" |
990 | + objectID + "\" reference list : \"" |
991 | + printStringList(selectedObjectIDList) + "\". " |
992 | + e.getMessage(), e); |
993 | } |
994 | |
995 | throw convertToSerializableException(e); |
996 | } |
997 | |
998 | if (LOGGER.isLoggable(Level.FINE)) |
999 | { |
1000 | LOGGER.log(Level.FINE, |
1001 | "updateReferenceList exit success : collection ID : \"" |
1002 | + objectID + "\" reference list : \"" |
1003 | + printStringList(selectedObjectIDList) + "\"."); |
1004 | } |
1005 | } |
1006 | |
1007 | /** |
1008 | * convert an exception to one that can be serialized in the GWT layer |
1009 | * |
1010 | * @param e the exception to convert |
1011 | * @return the converted exception. |
1012 | */ |
1013 | public Exception convertToSerializableException( |
1014 | Exception e) |
1015 | { |
1016 | return new Exception(e.getMessage()); |
1017 | } |
1018 | |
1019 | /** |
1020 | * |
1021 | * print String List |
1022 | * |
1023 | * @param stringList list to print |
1024 | * @return the printed string list |
1025 | */ |
1026 | public static String printStringList( |
1027 | List<String> stringList) |
1028 | { |
1029 | if (stringList == null) |
1030 | { |
1031 | return "null"; |
1032 | } |
1033 | |
1034 | StringBuilder build = new StringBuilder(); |
1035 | |
1036 | build.append("[ "); |
1037 | |
1038 | for (String string : stringList) |
1039 | { |
1040 | build.append(string); |
1041 | build.append(" "); |
1042 | } |
1043 | |
1044 | build.append("]"); |
1045 | |
1046 | return build.toString(); |
1047 | } |
1048 | |
1049 | /** |
1050 | * run Update Reference List |
1051 | * |
1052 | * @param objectID the object ID to update |
1053 | * @param referencedObjectIDList the new reference list |
1054 | * @throws Exception any error encountered during the update |
1055 | */ |
1056 | protected void runUpdateReferenceList( |
1057 | String objectID, List<String> referencedObjectIDList) throws Exception |
1058 | { |
1059 | try |
1060 | { |
1061 | PropertyCollection collection = getCollectionFromStingID(objectID); |
1062 | |
1063 | if (collection == null) |
1064 | { |
1065 | throw new IllegalArgumentException( |
1066 | "Unable to find collection of ID : \"" + objectID |
1067 | + "\" for which to update the reference list."); |
1068 | } |
1069 | List<PropertyCollection<?, ?>> selectionList = |
1070 | new ArrayList<PropertyCollection<?, ?>>(); |
1071 | |
1072 | if (referencedObjectIDList != null) |
1073 | { |
1074 | for (String selectedID : referencedObjectIDList) |
1075 | { |
1076 | PropertyCollection selectedCollection = |
1077 | getCollectionFromStingID(selectedID); |
1078 | |
1079 | if (selectedCollection == null) |
1080 | { |
1081 | throw new IllegalArgumentException( |
1082 | "Unable to find collection of ID : \"" |
1083 | + selectedID |
1084 | + "\" that should be placed in the selection list of collection ID \"" |
1085 | + objectID + "\""); |
1086 | } |
1087 | selectionList.add(selectedCollection); |
1088 | } |
1089 | } |
1090 | |
1091 | DeductionRuleLibrary.fillList(collection, selectionList, true); |
1092 | } |
1093 | catch (Exception e) |
1094 | { |
1095 | AssertUtilities.exception(e); |
1096 | throw e; |
1097 | } |
1098 | } |
1099 | |
1100 | /** |
1101 | * get Collection From Sting ID |
1102 | * |
1103 | * @param objectID the string object ID |
1104 | * @return the matching collection |
1105 | */ |
1106 | public PropertyCollection<?, ?> getCollectionFromStingID( |
1107 | String objectID) |
1108 | { |
1109 | PropertyCollection<?, ?> retVal = null; |
1110 | try |
1111 | { |
1112 | Long longObjectID = convertStringToObjectID(objectID); |
1113 | retVal = listener.getCollection(longObjectID); |
1114 | } |
1115 | catch (Exception e) |
1116 | { |
1117 | AssertUtilities.exception(e); |
1118 | } |
1119 | return retVal; |
1120 | } |
1121 | |
1122 | /** |
1123 | * convert String To ObjectID |
1124 | * |
1125 | * @param objectID the object ID string |
1126 | * @return the integer object ID |
1127 | */ |
1128 | public static Long convertStringToObjectID( |
1129 | String objectID) |
1130 | { |
1131 | return Long.valueOf(objectID); |
1132 | } |
1133 | |
1134 | /** |
1135 | * (non-JSDoc) |
1136 | * |
1137 | * @see org.deduced.viewer.web.shared.DynamicUserInterfaceService#signalTriggered(java.lang.String, |
1138 | * java.lang.String) |
1139 | */ |
1140 | @Override |
1141 | public void signalTriggered( |
1142 | String objectID, final String signalName) throws Exception |
1143 | { |
1144 | if (LOGGER.isLoggable(Level.FINE)) |
1145 | { |
1146 | LOGGER.log(Level.FINE, "signalTriggered entry : collection ID : \"" |
1147 | + objectID + "\" signal named : \"" + signalName + "\"."); |
1148 | } |
1149 | |
1150 | final PropertyCollection collection = |
1151 | getCollectionFromStingID(objectID); |
1152 | |
1153 | if (collection == null) |
1154 | { |
1155 | IllegalArgumentException iae = |
1156 | new IllegalArgumentException( |
1157 | "signalTriggered : Unable to find collection ID : \"" |
1158 | + objectID |
1159 | + "\" for which to trigger the signal named : \"" |
1160 | + signalName + "\"."); |
1161 | |
1162 | if (LOGGER.isLoggable(Level.FINE)) |
1163 | { |
1164 | LOGGER.log(Level.FINE, |
1165 | "signalTriggered exit failed : " + iae.getMessage(), iae); |
1166 | } |
1167 | |
1168 | throw convertToSerializableException(iae); |
1169 | } |
1170 | |
1171 | try |
1172 | { |
1173 | requestRunner.run(new ExceptionRunnable() |
1174 | { |
1175 | @Override |
1176 | public void runAndThrow() throws Exception |
1177 | { |
1178 | collection.invokeAction(signalName, null); |
1179 | } |
1180 | }); |
1181 | } |
1182 | catch (Exception e) |
1183 | { |
1184 | IllegalArgumentException iae = |
1185 | new IllegalArgumentException( |
1186 | "signalTriggered failed. Collection ID : \"" + objectID |
1187 | + "\" named \"" |
1188 | + DeducedUtilities.getCollectionName(collection) |
1189 | + "\" of type \"" |
1190 | + DeducedUtilities.getCollectionName(collection.type()) |
1191 | + "\". Signal named : \"" + signalName + "\". Error : " |
1192 | + e.getMessage()); |
1193 | |
1194 | if (LOGGER.isLoggable(Level.FINE)) |
1195 | { |
1196 | LOGGER.log(Level.FINE, |
1197 | "signalTriggered exit failed : " + iae.getMessage(), iae); |
1198 | } |
1199 | |
1200 | throw convertToSerializableException(iae); |
1201 | } |
1202 | |
1203 | if (LOGGER.isLoggable(Level.FINE)) |
1204 | { |
1205 | LOGGER.log(Level.FINE, |
1206 | "signalTriggered exit success : collection ID : \"" + objectID |
1207 | + "\" signal named : \"" + signalName + "\"."); |
1208 | } |
1209 | } |
1210 | } |