1 /* ====================================================================
2 * Bigyo Software License, version 1.1
3 *
4 * Copyright (c) 2004, Zsombor Gegesy. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * 3. Neither the name of the Bigyo Group nor the name "Bigyo" nor
18 * the names of its contributors may be used to endorse or promote
19 * products derived from this software without specific prior
20 * written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 *
35 * ====================================================================
36 */
37
38
39 package net.sf.bigyo.container;
40
41 import java.io.BufferedInputStream;
42 import java.io.BufferedOutputStream;
43 import java.io.File;
44 import java.io.FileFilter;
45 import java.io.FileInputStream;
46 import java.io.FileNotFoundException;
47 import java.io.FileOutputStream;
48 import java.io.IOException;
49 import java.io.InputStreamReader;
50 import java.io.OutputStreamWriter;
51 import java.io.UnsupportedEncodingException;
52 import java.util.ArrayList;
53 import java.util.HashMap;
54 import java.util.Iterator;
55 import java.util.List;
56 import java.util.Map;
57
58 import net.sf.bigyo.api.ContainerException;
59 import net.sf.bigyo.api.ReconfigurationManager;
60 import net.sf.bigyo.container.api.Constants;
61 import net.sf.bigyo.container.api.Repository;
62 import net.sf.bigyo.container.config.ConfigurationStrategy;
63 import net.sf.bigyo.container.config.ReconfigurationNotSupportedException;
64 import net.sf.bigyo.container.config.SimpleConfigurationStrategy;
65 import net.sf.bigyo.model.ComponentConfig;
66 import net.sf.bigyo.model.ObjectDependency;
67
68 import org.apache.avalon.fortress.util.dag.CyclicDependencyException;
69 import org.apache.avalon.fortress.util.dag.DirectedAcyclicGraphVerifier;
70 import org.apache.avalon.fortress.util.dag.Vertex;
71 import org.apache.log4j.LogManager;
72 import org.apache.log4j.Logger;
73
74 import com.thoughtworks.xstream.converters.ConversionException;
75 import com.thoughtworks.xstream.io.StreamException;
76
77 /***
78 * RepositoryImpl manages the component instances, (create, configure,
79 * reload...stop)
80 *
81 * Created on 2004.04.01.
82 * @author zsombor
83 *
84 */
85 class RepositoryImpl implements Repository, ReconfigurationManager {
86
87 final static Logger LOG = LogManager.getLogger(Main.class);
88
89 boolean panicIfDuplicates = true;
90 boolean createBackups = true;
91
92 List vertices = new ArrayList();
93 Main main;
94
95 Map vertMap = new HashMap();
96
97 List compConfigs = new ArrayList();
98 Map componentConfigMapping = new HashMap();
99 List startupComponents = new ArrayList();
100 Map fileToConfigMap = new HashMap();
101 Map componentToPersistedSource = new HashMap();
102
103 private ConfigurationStrategy configurationStrategy;
104
105 static String xmlFileEncoding = "ISO-8859-1";
106
107 static {
108 xmlFileEncoding = System.getProperty("xml.file.encoding", xmlFileEncoding);
109 LOG.info("xml file encoding set to " + xmlFileEncoding);
110 }
111
112 /***
113 *
114 */
115 RepositoryImpl(Main m) {
116 this.main = m;
117 this.configurationStrategy = new SimpleConfigurationStrategy();
118 ComponentConfig serviceLocator = new ComponentConfig();
119 serviceLocator.setInstanceName(Constants.SERVICE_LOCATOR);
120 serviceLocator.setClassAlias(Constants.SERVICE_LOCATOR);
121 addConfig(null, serviceLocator);
122
123 ComponentConfig reconfManager = new ComponentConfig();
124 reconfManager.setInstanceName(Constants.RECONFIGURATION_MANAGER);
125 reconfManager.setClassAlias(Constants.RECONFIGURATION_MANAGER);
126 addConfig(null, reconfManager);
127 }
128
129 /***
130 * betolti az adott konyvtarban levo osszes .conf.xml -re vegzodo fajlt.
131 *
132 * @param directory
133 * @throws IOException
134 */
135 public void loadConfigurations(File directory) throws ContainerException {
136 /*
137 * if (this.directory !=null) throw new
138 * ContainerException("Configuration directory already
139 * set!["+this.directory+']');
140 */
141 if (!directory.exists())
142 throw new ContainerException("Directory not exists![" + directory + ']');
143 if (!directory.isDirectory())
144 throw new ContainerException("File is not a directory![" + directory + ']');
145 File[] comp = listFiles(directory);
146 LOG.info("found " + comp.length + " component configurations");
147 for (int i = 0; i < comp.length; i++) {
148
149 try {
150 ComponentConfig config = createConfig(comp[i]);
151
152 insertConfiguration(comp[i], config);
153
154 } catch (FileNotFoundException e) {
155 LOG.warn("Strange error :" + e.getMessage(), e);
156 } catch (UnsupportedEncodingException e) {
157 LOG.warn("Encoding exception, specify correct value for xml.file.encoding!", e);
158 throw new ContainerException("Encoding exception, specify correct value for xml.file.encoding!", e);
159 }
160 }
161
162 }
163
164 /***
165 * A configuraciot be regisztralja ebbe a repositoryba, mint amit kesobb el
166 * lehet inicalizalni, ellenorzi, hogy jo neve van, s hogy a megfelelo
167 * profile-ban van e
168 *
169 * @param file
170 * @param config
171 * @return false - if component not in the current profile list
172 * @throws ContainerException
173 */
174 private boolean insertConfiguration(File file, ComponentConfig config) throws ContainerException {
175 if (!Constants.validComponentName(config.getInstanceName()))
176 throw new ContainerException("Component name '" + config.getInstanceName() + "' is not allowed!");
177 if (config.getProfile() != null) {
178 if (!main.hasProfile(config.getProfile())) {
179 LOG.info("Component " + config.getInstanceName() + " profile is " + config.getProfile() + ", skip");
180 return false;
181 }
182 }
183 if (this.componentConfigMapping.containsKey(config.getInstanceName())) {
184 if (panicIfDuplicates)
185 throw new ContainerException("There is already a component named '" + config.getInstanceName()
186 + "' loaded!");
187 LOG.warn("There is already a component named '" + config.getInstanceName() + "' loaded, skipeed");
188 return false;
189 }
190
191 addConfig(file, config);
192 LOG.info("from " + file + " " + config.getInstanceName() + ", style:" + config.getLifeCycle());
193 if (config.isStartupComponent()) {
194 startupComponents.add(config.getInstanceName());
195 }
196
197 return true;
198 }
199
200 /***
201 * @param file
202 * @return @throws
203 * UnsupportedEncodingException
204 * @throws FileNotFoundException
205 */
206 private ComponentConfig createConfig(File file) throws UnsupportedEncodingException, FileNotFoundException,
207 StreamException, ConversionException {
208 ComponentConfig config = (ComponentConfig) main.xstream.fromXML(new InputStreamReader(new BufferedInputStream(
209 new FileInputStream(file)), xmlFileEncoding));
210 // init configuration (create the necessary proxy class, or substitute
211 // with an other config...)
212 config.setConfigBean(configurationStrategy.initConfigurationBean(config.getConfigBean()));
213 return config;
214 }
215
216 private PersistedComponentConfig getConfigSource(File file) {
217 return (PersistedComponentConfig) fileToConfigMap.get(file.getAbsolutePath());
218 }
219
220 /***
221 * Check the component directory for new components
222 *
223 * @param directory
224 * @return the list of the new 'startup' components.
225 * @throws UnsupportedEncodingException
226 * @throws FileNotFoundException
227 * @throws ContainerException
228 */
229 public List refreshComponents(File directory) throws ContainerException {
230 File[] curComp = listFiles(directory);
231 List result = new ArrayList();
232 List newVertexList = new ArrayList();
233 for (int i = 0; i < curComp.length; i++) {
234 try {
235 PersistedComponentConfig configSource = getConfigSource(curComp[i]);
236 if (configSource != null) {
237 // already deployed, check for reconfiguration ...
238 if (configurationStrategy.isModifiedSince(configSource.getConfig())) {
239 persistComponentConfig(configSource);
240 } else if (configSource.isModifiedSince()) {
241 // config modified in the file system
242 LOG.info("configuration modified in filesystem [" + configSource.getConfig().getInstanceName()
243 + ']');
244 ComponentConfig newConfig = createConfig(curComp[i]);
245 // TODO: implement!
246 // 1, proxy-t generaljunk a configBean-ek koze, s itt
247 // csak szimplan lecsereljuk a benne levot
248 // 2, uj interfacet adunk a megfelelo komponensekhez,
249 // amiken keresztul ertesul a modosulasokrol
250 // 3, reflection-nel bemasoljuk a modosult property-ket
251 // az eredeti beanbe
252
253 //Object obj =
254 // main.getComponent(configSource.getConfig().getInstanceName());
255
256 reconfigureComponent(configSource, newConfig);
257 }
258 } else {
259 ComponentConfig config = createConfig(curComp[i]);
260 if (insertConfiguration(curComp[i], config)) {
261 result.add(config.getInstanceName());
262 newVertexList.add(vertMap.get(config.getInstanceName()));
263 }
264 }
265 } catch (FileNotFoundException e) {
266 LOG.warn("Strange error :" + e.getMessage(), e);
267 } catch (StreamException e) {
268 LOG.warn("Not well formed xml :" + e.getMessage(), e);
269 throw new ContainerException("Not well formed xml:" + e.getMessage(), e);
270 } catch (ConversionException e) {
271 LOG.warn("Not well formed xml :" + e.getMessage(), e);
272 throw new ContainerException("Not well formed xml:" + e.getMessage(), e);
273 } catch (UnsupportedEncodingException e) {
274 LOG.warn("Encoding exception, specify correct value for xml.file.encoding!", e);
275 throw new ContainerException("Encoding exception, specify correct value for xml.file.encoding!", e);
276 }
277
278 }
279 sortVertices(newVertexList);
280 return result;
281 }
282
283 /***
284 * @param configSource
285 * @throws UnsupportedEncodingException
286 * @throws FileNotFoundException
287 */
288 protected void persistComponentConfig(PersistedComponentConfig configSource) throws UnsupportedEncodingException, FileNotFoundException {
289 // config modified by the component
290 if (createBackups) {
291 LOG.info("configuration modified by component["
292 + configSource.getConfig().getInstanceName() + "]: create backup");
293 configSource.createBackup();
294 } else {
295 LOG.info("configuration modified by component["
296 + configSource.getConfig().getInstanceName() + "]: skip backup creation");
297 }
298 ComponentConfig config = configSource.getConfig();
299 // todo, ugly ... not thread safe ...
300 Object wrapped = config.getConfigBean();
301 Object orig = configurationStrategy.getSerializableConfigurationBean(wrapped);
302 config.setConfigBean(orig);
303 saveComponentConfig(config, configSource.getFile());
304 config.setConfigBean(wrapped);
305
306 // reset the last modified date to the current time
307 configSource.isModifiedSince();
308 }
309
310 /***
311 * @param configSource
312 * the persistent storage of the configuration
313 * @param newConfig
314 * the new configuration.
315 * @throws ContainerException
316 * if the component not found. Probably some concurrency error.
317 */
318 protected void reconfigureComponent(PersistedComponentConfig configSource, ComponentConfig newConfig)
319 throws ContainerException {
320 try {
321 Object obj = main.getComponent(configSource.getConfig().getInstanceName());
322 Object oldConfig = configSource.getConfig().getConfigBean();
323
324 configurationStrategy.reconfigureService(obj, configSource.getConfig(), newConfig);
325 configSource.setConfig(newConfig);
326 registerComponentConfig(newConfig);
327
328 main.notifyReconfigurationListeners(obj, oldConfig, newConfig);
329 } catch (ReconfigurationFailedException rfe) {
330 LOG.warn("Reconfiguration failed:" + rfe.getMessage(), rfe);
331 } catch (ReconfigurationNotSupportedException rfe) {
332 LOG.warn("Reconfiguration failed:" + rfe.getMessage(), rfe);
333 }
334 }
335
336 /***
337 *
338 * @param newConfig
339 */
340 public void reconfigureComponent(ComponentConfig newConfig) {
341 try {
342 String instanceName = newConfig.getInstanceName();
343 Object obj = main.getComponent(instanceName);
344 PersistedComponentConfig configSource = getPersistedComponentConfig(instanceName);
345 ComponentConfig oldConfig = newConfig;
346 if (configSource != null) {
347 oldConfig = configSource.getConfig();
348 }
349 configurationStrategy.reconfigureService(obj, oldConfig, newConfig);
350 if (configSource != null) {
351 configSource.setConfig(newConfig);
352 try {
353 persistComponentConfig(configSource);
354 } catch (UnsupportedEncodingException e) {
355 LOG.warn("Reconfiguration save failed:" + e.getMessage(), e);
356 } catch (FileNotFoundException e) {
357 LOG.warn("Reconfiguration save failed:" + e.getMessage(), e);
358 }
359 }
360 registerComponentConfig(newConfig);
361 main.notifyReconfigurationListeners(obj, oldConfig.getConfigBean(), newConfig);
362
363
364 } catch (ReconfigurationFailedException rfe) {
365 LOG.warn("Reconfiguration failed:" + rfe.getMessage(), rfe);
366 } catch (ReconfigurationNotSupportedException rfe) {
367 LOG.warn("Reconfiguration failed:" + rfe.getMessage(), rfe);
368 } catch (ContainerException e) {
369 LOG.warn("Reconfiguration failed:" + e.getMessage(), e);
370 }
371 }
372
373
374 private static final class ConfigFileFilter implements FileFilter {
375
376 /*
377 * (non-Javadoc)
378 *
379 * @see java.io.FileFilter#accept(java.io.File)
380 */
381 public boolean accept(File file) {
382 boolean acc = (file.getName().endsWith(".conf.xml"));
383 if (acc) {
384 LOG.info("component file " + file.getName());
385 } else {
386 LOG.warn("Not a component descriptor file! ('"+file.getName()+"' not ends with '.conf.xml')");
387 }
388 return acc;
389 }
390 }
391
392 /***
393 * @param directory
394 * @return
395 */
396 private File[] listFiles(File directory) {
397 return directory.listFiles(new ConfigFileFilter());
398 }
399
400 /***
401 * Eltarolja, a konfiguracio forrasat.
402 *
403 * @param file
404 * @param config
405 */
406 private void addConfig(File file, ComponentConfig config) {
407 if (file != null) {
408 PersistedComponentConfig pcc = new PersistedComponentConfig(file, config);
409 fileToConfigMap.put(file.getAbsolutePath(), pcc);
410 componentToPersistedSource.put(config.getInstanceName(), pcc);
411 }
412 compConfigs.add(config);
413 registerComponentConfig(config);
414
415 Vertex v = new Vertex(config.getInstanceName(), config);
416 vertMap.put(v.getName(), v);
417 vertices.add(v);
418
419 }
420
421 private PersistedComponentConfig getPersistedComponentConfig(String instanceName) {
422 return (PersistedComponentConfig) componentToPersistedSource.get(instanceName);
423 }
424
425 /***
426 * @param config
427 */
428 private void registerComponentConfig(ComponentConfig config) {
429 this.componentConfigMapping.put(config.getInstanceName(), config);
430 }
431
432 /***
433 * visszaadja a megadott nevu instancehez tartozo konfiguraciot
434 *
435 * @param instanceName
436 * @return
437 */
438 public ComponentConfig getComponentConfig(String instanceName) {
439 return (ComponentConfig) this.componentConfigMapping.get(instanceName);
440 }
441
442 /***
443 * sorba rendezi az osszes komponens, hogy jol inicalizalni lehessen oket.
444 * Valojaban arra kell, hogy megallapitsuk, hogy nincs benne ciklikus
445 * fuggoseg.
446 *
447 * @throws ContainerException
448 */
449 void run() throws ContainerException {
450 sortVertices(vertices);
451 }
452
453 /***
454 * a listaban levo Vertex -ekre megallapitja a fuggosegeket, s sorba rendezi
455 * a vertices listaba. newVerticesList reszhalmaza a vertices- nek !
456 *
457 * @param newVerticesList
458 * az uj,sorba rendezendo komponensek listaja (Vertex- eket
459 * tartalmaz)
460 * @throws ContainerException
461 */
462 void sortVertices(List newVerticesList) throws ContainerException {
463 // iranyitasok letrehozasa
464 assert newVerticesList != null : "list must not be null";
465 for (int i = 0; i < newVerticesList.size(); i++) {
466 Vertex v = (Vertex) newVerticesList.get(i);
467 ComponentConfig c = (ComponentConfig) v.getNode();
468 List deps = c.getNeededObjects();
469 if (deps != null) {
470 for (Iterator iter = deps.iterator(); iter.hasNext();) {
471 ObjectDependency dep = (ObjectDependency) iter.next();
472 Vertex v2 = (Vertex) vertMap.get(dep.getObjectName());
473 if (v2 == null)
474 throw new ContainerException("No component found as '" + dep.getObjectName()
475 + "', it's required dependency of '" + c.getInstanceName() + '\'');
476 v.addDependency(v2);
477 }
478 }
479 // component with not valid name, (the built in, container provided
480 // components), do not need componentDescription
481 if (Constants.validComponentName(c.getInstanceName())) {
482 ComponentDescription desc = main.getComponentDescription(c);
483 if (desc == null)
484 throw new ContainerException("No description found for " + c.getInstanceName() + ", from class:"
485 + c.getClassAlias());
486 if (desc.getDepends() != null) {
487 for (Iterator iter = desc.getDepends().iterator(); iter.hasNext();) {
488 ClassDependency dep = (ClassDependency) iter.next();
489 if (dep.getDependencyType() != null) {
490 List objects = getObjectsFor(dep.getClassAlias());
491 if (ClassDependency.AFTER.equals(dep.getDependencyType())) {
492 for (Iterator it2 = objects.iterator(); it2.hasNext();) {
493 ComponentConfig config = (ComponentConfig) it2.next();
494 Vertex v2 = (Vertex) vertMap.get(config.getInstanceName());
495 if (v2 == null)
496 throw new RuntimeException("it's impossible");
497 v.addDependency(v2);
498 }
499 }
500 if (ClassDependency.BEFORE.equals(dep.getDependencyType())) {
501 for (Iterator it2 = objects.iterator(); it2.hasNext();) {
502 ComponentConfig config = (ComponentConfig) it2.next();
503 Vertex v2 = (Vertex) vertMap.get(config.getInstanceName());
504 if (v2 == null)
505 throw new RuntimeException("it's impossible");
506 v2.addDependency(v);
507 }
508 }
509 if (ClassDependency.REQUIRED.equals(dep.getDependencyType())) {
510 if (objects.size() == 0)
511 throw new ContainerException("No component fullfills the required dependency '"
512 + dep.getClassAlias() + "' for '" + c.getInstanceName() + "'");
513 }
514
515 }
516 }
517 }
518 }
519 }
520 try {
521 DirectedAcyclicGraphVerifier.topologicalSort(vertices);
522 } catch (CyclicDependencyException e) {
523 LOG.warn("Component tree is cylic!" + e.getMessage(), e);
524
525 throw new ContainerException("Component tree is cylic!" + e.getMessage(), e);
526 }
527 LOG.info("component tree is acylic, order " + Util.convertVertexList(vertices));
528
529 }
530
531 /***
532 * @return a list of objects which provides the specified
533 * interface/contract.
534 */
535 public List getObjectsFor(String classAlias) {
536 ArrayList result = new ArrayList(compConfigs.size());
537 for (Iterator iter = compConfigs.iterator(); iter.hasNext();) {
538 ComponentConfig c = (ComponentConfig) iter.next();
539 if (classAlias.equals(c.getClassAlias())) {
540 result.add(c);
541 } else {
542 if (Constants.validComponentName(c.getInstanceName())) {
543 ComponentDescription cd = main.getComponentDescription(c);
544 if (cd == null) {
545 LOG.warn("description not found for " + c.getInstanceName() + "['" + c.getClassAlias() + "']");
546 } else {
547 if (cd.getProvides() != null) {
548 LOG.debug("check " + cd.getClassAlias() + " if provides " + classAlias);
549 for (Iterator provides = cd.getProvides().iterator(); provides.hasNext();) {
550 String provide = (String) provides.next();
551 if (classAlias.equals(provide)) {
552 result.add(c);
553 break;
554 }
555 }
556 }
557 }
558 }
559 }
560 }
561 return result;
562 }
563
564 /***
565 * helper method to store the component config to a custom file
566 *
567 * @param config
568 * @param file
569 * @throws UnsupportedEncodingException
570 * @throws FileNotFoundException
571 */
572 public void saveComponentConfig(ComponentConfig config, File file) throws UnsupportedEncodingException,
573 FileNotFoundException {
574 main.xstream.toXML(config, new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(file)),
575 xmlFileEncoding));
576 }
577
578 public void saveConfigurations(File directory) throws ContainerException {
579 if (!directory.exists())
580 throw new ContainerException("Directory not exists![" + directory + ']');
581 if (!directory.isDirectory())
582 throw new ContainerException("File is not a directory![" + directory + ']');
583 for (Iterator iter = compConfigs.iterator(); iter.hasNext();) {
584 ComponentConfig config = (ComponentConfig) iter.next();
585 try {
586 saveComponentConfig(config, new File(directory, config.getInstanceName() + ".conf.xml"));
587 } catch (FileNotFoundException e) {
588 LOG.warn("Strange error :" + e.getMessage(), e);
589 } catch (UnsupportedEncodingException e) {
590 LOG.warn("Encoding exception, specify correct value for xml.file.encoding!", e);
591 throw new ContainerException("Encoding exception, specify correct value for xml.file.encoding!", e);
592 }
593 }
594 }
595
596 /***
597 * @param panicIfDuplicates
598 * The panicIfDuplicates to set.
599 */
600 public void setPanicIfDuplicateFound(boolean panicIfDuplicates) {
601 this.panicIfDuplicates = panicIfDuplicates;
602 }
603
604 /***
605 * @return Returns the configurationStrategy.
606 */
607 public ConfigurationStrategy getConfigurationStrategy() {
608 return configurationStrategy;
609 }
610
611 /***
612 * @param configurationStrategy
613 * The configurationStrategy to set.
614 */
615 public void setConfigurationStrategy(ConfigurationStrategy configurationStrategy) {
616 this.configurationStrategy = configurationStrategy;
617 }
618
619 /***
620 * @param createBackups
621 * The createBackups to set.
622 */
623 public void setCreateBackups(boolean createBackups) {
624 this.createBackups = createBackups;
625 }
626
627 /***
628 * @return Returns the createBackups.
629 */
630 public boolean isCreateBackups() {
631 return createBackups;
632 }
633 }
This page was automatically generated by Maven