Ostatnimi czasy często spotykam się z problemem konieczności stworzenia kopii JavaBean'a. O ile dla Javy napisano już tysiące API to jednak do tej prostej czynności wciąż nie ma rozwiązania idealnego.
Omówię teraz pokrótce o co mi chodzi. Przede wszystkim API takie powinno dostarczać następujące funkcjonalności:
- kopiować wgłąb (tzw. deep copy) - kopiowanie referencji nie wchodzi w grę gdyż nie jest to wtedy tak naprawdę operacja kopiowania
- nie kopiować n razy tej samej referencji - jeśli w oryginalnym obiekcie znajduje się wiele referencji do 1 obiektu, to w kopii też powinno być wiele referencji do skopiowanego obiektu
- radzić sobie z cyklami - w przypadku wystąpienia cyklu nie powinien wyrzucać wyjątków
- wykorzystywać dostarczony ClassLoader do wczytywania klas - tak aby można było dostarczyć zmodyfikowane klasy
Na www znalazłem kilka rozwiązań. Między innymi dobrze znane Commons Beanutils. Niestety ono nie spełnia ani jednego wymogu. Drugą biblioteką, która mi wpadła do rąk była Sojo. API jest w fazie rozwoju ale już teraz udostępnia wiele funkcjonalności. Obsługuje kopiowanie wgłąb i radzi sobie z cyklami. Nie kopiuje też uprzednio skopiowanej referencji. Niestety nie znalazłem sposobu na dostarczenie własnego ClassLoadera.
Ze względu na brak wymarzonej biblioteki zacząłem szukać alternatywnych rozwiązań. Pierwszą techniką, którą wykorzystałem była wbudowana serializacja. Niestety ta metoda nie pozwala na dostarczenie własnego ClassLoadera (wykorzystywany jest ClassLoader wywołujący metodę deserializacji) i wymaga by każda klasa implementowała interfejs Serializable.
Kolejnym rozwiązaniem, które przetestowałem było użycie koderów JavaBean<->XML dostarczanych wraz z JRE (klasy XMLEncoder, XMLDecoder). Technika ta jest zdecydowanie najwolniejsza, jednak na pierwszy rzut oka wydawało się, że posiada wszystkie cechy, które od takiego API wymagam. Niestety pomimo dostarczenia własnego ClassLoader'a nie jest on wykorzystywany do wczytania wszystkich niezbędnych klas.
Po kilku dniach próbowania różnych technologii stwierdziłem, że szybciej będzie jak sam zaimplementuję taką funkcjonalność. I w ten oto sposób w 2 dni napisałem proste API do kopiowania beanów ze wszystkimi funkcjonalnościami. API nosi roboczą nazwę BeanCopier i na razie dostępne jest do ściągnięcia na repozytorium Maven 2 pod adresem: http://eggframework.org/maven2.
Aby użyć API we własnym projekcie Maven 2 należy dodać do pom.xml:
<project> <dependencies> <dependency> <groupId>com.jacekolszak</groupId> <artifactId>beancopier</artifactId> <version>0.9</version> </dependency> </dependencies> <repositories> <repository> <id>Egg Framework</id> <url>http://eggframework.org/maven2</url> </repository> </repositories> </project>
Sposób użycia BeanCopier:
BeanCopier beanCopier = new BeanCopierImpl(); copy = beanCopier.copy(original, classLoader);
Niebawem wrzucę źródła na CVS. Póki co są one dostępne w jarze beancopier-0.9-sources.jar na repozytorium Maven 2.
UPDATE: Źródła są dostępne na repozytorium Bazaar po adresem: http://bazaar.launchpad.net/~jacekolszak/beancopier/trunk. Wystarczy wydać polecenie:
bzr branch http://bazaar.launchpad.net/~jacekolszak/beancopier/trunk
aby ściągnąć najnowsze źródła.