1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.seasar.tuigwaa.database;
17
18 import java.io.InputStream;
19 import java.sql.Connection;
20 import java.sql.ResultSet;
21 import java.sql.ResultSetMetaData;
22 import java.sql.SQLException;
23 import java.sql.Statement;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32
33 import javax.sql.DataSource;
34 import javax.sql.XADataSource;
35 import javax.transaction.TransactionManager;
36
37 import org.apache.commons.beanutils.DynaBean;
38 import org.apache.commons.beanutils.DynaProperty;
39 import org.apache.commons.collections.CollectionUtils;
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42 import org.seasar.extension.dbcp.impl.ConnectionPoolImpl;
43 import org.seasar.extension.dbcp.impl.DataSourceImpl;
44 import org.seasar.extension.dbcp.impl.XADataSourceImpl;
45 import org.seasar.framework.exception.SQLRuntimeException;
46 import org.seasar.tuigwaa.database.util.CSVfileLoader;
47 import org.seasar.tuigwaa.database.util.DatafileLoader;
48 import org.seasar.tuigwaa.database.util.DynaBatchHandler;
49 import org.seasar.tuigwaa.database.util.DynaPropertyWrapper;
50 import org.seasar.tuigwaa.database.util.DynaSelectHandler;
51 import org.seasar.tuigwaa.database.util.EntityFactory;
52 import org.seasar.tuigwaa.model.common.DomainUtils;
53 import org.seasar.tuigwaa.system.Constants;
54 import org.seasar.tuigwaa.system.TgwServiceException;
55 import org.seasar.tuigwaa.util.TgwPathResolver;
56 import org.seasar.tuigwaa.util.TgwResource;
57
58 import com.isenshi.util.ResourceUtils;
59 import com.isenshi.util.extlib.DiconResource;
60
61 /***
62 * Need to fix the policy of byte array data backup/restore.
63 *
64 * @author someda
65 */
66 public class BasicDatabaseServiceImpl implements BasicDatabaseService {
67
68 private static final String TRANSACTION_DICON = "dicon/transaction.dicon";
69
70 private MultiDataSource multiDataSource;
71
72 private Log log_ = LogFactory.getLog(getClass());
73
74 private Map databaseMap = new HashMap();
75
76 private Map schemaMap = new HashMap();
77
78 private Map driverMap = Collections.synchronizedMap(new HashMap());
79
80 private TransactionManager tmanager;
81
82 public BasicDatabaseServiceImpl(MultiDataSource multiDataSource,
83 DynaSelectHandler selectHandler, DynaBatchHandler batchHandler,
84 TransactionManager tmanager) {
85 this.multiDataSource = multiDataSource;
86
87
88 this.tmanager = tmanager;
89 }
90
91 public String getDriver(String domainName) {
92 String databaseName = getDataBaseName(domainName);
93 String driver = (String) driverMap.get(databaseName);
94 if (driver != null) {
95 return driver;
96 } else {
97 return TgwResource.getProperty("database.driver");
98 }
99 }
100
101 public boolean hasDomain(String databaseName) {
102 Iterator itr = databaseMap.keySet().iterator();
103 while(itr.hasNext()){
104 String domainName = (String)itr.next();
105 String aDatabaseName = (String)databaseMap.get(domainName);
106 if(databaseName.equals(aDatabaseName)){
107 return true;
108 }
109 }
110 return false;
111 }
112
113 public String getSchemaName(String domainName) {
114 String schemaName = (String) schemaMap.get(domainName);
115 if (schemaName != null) {
116 return schemaName;
117 }
118 return domainName;
119 }
120
121 public void addExternalDatabase(String dbName, String driver, String user,
122 String password, String url) {
123
124
125 XADataSourceImpl xaDataSource = new XADataSourceImpl();
126 xaDataSource.setDriverClassName(driver);
127 xaDataSource.setUser(user);
128 xaDataSource.setURL(url);
129 xaDataSource.setPassword(password);
130 ConnectionPoolImpl pool = new ConnectionPoolImpl();
131 pool.setXADataSource(xaDataSource);
132 pool.setTransactionManager(tmanager);
133 DataSourceImpl dataSource = new DataSourceImpl(pool);
134
135 String path = TgwPathResolver.getDatabaseFilePath(dbName);
136
137 DiconResource dicon = new DiconResource();
138 dicon.setNamespace(dbName);
139 dicon.setPath(path);
140 dicon.addComponent(xaDataSource, null, new String[] {
141 "driverClassName", "user", "URL", "password" });
142 dicon.addComponent(pool, null, new String[] {});
143 dicon.addComponent(dataSource, null, new String[] {});
144 dicon.save();
145
146 addDataSource(dataSource, dbName, driver);
147 }
148
149 public void loadExternalDatabase() {
150 String[] files = ResourceUtils.getChildrenNames(Constants.DIRECTORY_APP);
151 if (files == null) {
152 return;
153 }
154 for (int i = 0; i < files.length; i++) {
155 if (files[i].endsWith(".db")) {
156 DiconResource dicon = new DiconResource();
157 dicon.setPath(Constants.DIRECTORY_APP + files[i]);
158 dicon.load();
159
160 dicon.bindRoot();
161 dicon.include(TRANSACTION_DICON);
162
163 String name = dicon.getNamespace();
164 DataSource dataSource = (DataSource) dicon
165 .getComponent(DataSource.class);
166
167 XADataSource xa = (XADataSource) dicon
168 .getComponent(XADataSource.class);
169 String driverName = ((XADataSourceImpl) xa)
170 .getDriverClassName();
171
172 addDataSource(dataSource, name, driverName);
173 }
174 }
175 }
176
177 private void addDataSource(DataSource dataSource, String dbName, String driverName){
178 if (checkConnection(dataSource)) {
179 driverMap.put(dbName, driverName);
180 multiDataSource.addDataSource(dbName, dataSource);
181 }
182 }
183
184 public void deleteExternalDatabase(String databaseName) {
185 driverMap.remove(databaseName);
186 multiDataSource.removeDataSource(databaseName);
187 }
188
189 private boolean checkConnection(DataSource dataSource) {
190 boolean connFlag = false;
191 try {
192 Connection con = dataSource.getConnection();
193 con.close();
194 connFlag = true;
195 } catch (SQLException e) {
196 log_.warn("can't load external dabtabase");
197 }
198 return connFlag;
199 }
200
201 public boolean useBaseDatabase(String domainName) {
202 return databaseMap.get(domainName) == null;
203 }
204
205 public void setExternalDatabaseMapping(String domainName, String dbName, String schemaName) throws TgwServiceException{
206 if(!multiDataSource.isExistDatabase(dbName)){
207 throw new TgwServiceException("doesn't exit database " + dbName);
208 }
209 putDataBaseName(domainName, dbName);
210 if (schemaName != null) {
211 schemaMap.put(domainName, schemaName);
212 }
213 }
214
215 public void removeExternalDatabaseMapping(String domainName) {
216 removeDataBaseName(domainName);
217 schemaMap.remove(domainName);
218 }
219
220 public List getTableNames(String domainName) {
221 DatabaseInfo info = getDatabaseInfo(domainName);
222 String schema = getSchemaName(domainName);
223 return info.getTablenameNames(schema);
224 }
225
226 public List getSchemaNames(String domainName) {
227 DatabaseInfo info = getDatabaseInfo(domainName);
228 return info.getSchemaNames();
229 }
230
231 public boolean existSchema(String schema) {
232 Iterator itr = getSchemaNames(schema).iterator();
233 boolean flag = false;
234 while (itr.hasNext()) {
235 String schemaName = (String) itr.next();
236 if (schemaName.equalsIgnoreCase(schema)) {
237 flag = true;
238 break;
239 }
240 }
241 return flag;
242 }
243
244 public Map createEntityMap(String domainName) throws SQLException {
245 String schema = getSchemaName(domainName);
246 DatabaseInfo info = getDatabaseInfo(domainName);
247 EntityFactory factory = new EntityFactory(domainName, schema, info);
248 Connection con = getConnection(domainName);
249
250 try {
251 Statement stmt = con.createStatement();
252
253 for (Iterator i = info.getTablenameNames(schema).iterator(); i
254 .hasNext();) {
255 String tableName = (String) i.next();
256 String sql = DatabaseUtils.buildBackupQuery(schema, tableName);
257 ResultSet rs = stmt.executeQuery(sql);
258 ResultSetMetaData metadata = rs.getMetaData();
259
260 String entityName = tableName.toLowerCase();
261 factory.addEntity(metadata, entityName);
262 }
263 } catch (SQLException e) {
264 e.printStackTrace();
265 }
266 return factory.getCreatedEntityMap();
267 }
268
269 public boolean existTable(String domainName, String name) {
270 Iterator itr = getTableNames(domainName).iterator();
271 boolean flag = false;
272 while (itr.hasNext()) {
273 String tableName = (String) itr.next();
274 if (tableName.equalsIgnoreCase(name)) {
275 flag = true;
276 break;
277 }
278 }
279 return flag;
280 }
281
282 public DatabaseInfo getBaseDatabaseInfo() {
283 return getDatabaseInfo(multiDataSource.getBaseConnection());
284 }
285
286 public DatabaseInfo getDatabaseInfo(String domainName) {
287 try {
288 Connection con = getConnection(domainName);
289 return getDatabaseInfo(con);
290 } catch (SQLException e) {
291 throw new SQLRuntimeException(e);
292 }
293 }
294
295 public DatabaseInfo getDatabaseInfo(Connection conn) {
296 DatabaseInfo info = null;
297 try {
298 info = new DatabaseInfo(conn);
299 } catch (SQLException se) {
300 log_.error(se.getMessage());
301 } finally {
302 DatabaseUtils.closeQueitly(conn);
303 }
304 return info;
305 }
306
307 public String[] getExternalDatabaseNames() {
308 return multiDataSource.getExternalDatabaseNames();
309 }
310
311 public List getExternalDatabaseInfoList() {
312 String[] names = getExternalDatabaseNames();
313 List list = new ArrayList();
314 for (int i = 0; i < names.length; i++) {
315 try {
316 Connection conn = multiDataSource.getConnection(names[i]);
317 DatabaseInfo dbInfo = getDatabaseInfo(conn);
318 dbInfo.setName(names[i]);
319 list.add(dbInfo);
320 } catch (SQLException e) {
321
322 }
323 }
324 return list;
325 }
326
327 public void backup(String dbName, String schema, String[] tableNames, String targetdir) throws
328 TgwServiceException{
329
330 DatabaseInfo info = getDatabaseInfo(schema);
331 if(dbName != null){
332 try{
333 Connection con = multiDataSource.getConnection(dbName);
334 info = getDatabaseInfo(con);
335 }catch(SQLException se){
336 throw new TgwServiceException(se);
337 }
338 }
339
340 Collection backupTableNameList = null;
341 if(tableNames == null || tableNames.length == 0){
342 backupTableNameList = info.getTablenameNames(schema);
343 }else{
344 Collection c = new ArrayList(Arrays.asList(tableNames));
345 for(int i=0;i<tableNames.length;i++){
346 List list = info.getForeignKeys(null,schema,tableNames[i]);
347 c = CollectionUtils.union(c,list);
348 }
349 backupTableNameList = c;
350 }
351 DiconResource resource = new DiconResource();
352 resource.setPath(TgwPathResolver.getDatabaseBackupFilePath(targetdir));
353
354 for (Iterator i = backupTableNameList.iterator(); i.hasNext();) {
355 String table = (String) i.next();
356 log_.info("back up table... " + table);
357 backupTable(dbName, schema, table, resource, targetdir);
358 }
359 resource.save();
360 }
361
362 public void restore(String dbName, String schema, String[] tableNames, String srcdir)
363 throws TgwServiceException{
364
365
366
367
368
369 List processList = DomainUtils.getSortedTalbeNames(schema);
370
371 DiconResource resource = new DiconResource();
372 resource.setPath(TgwPathResolver.getDatabaseBackupFilePath(srcdir));
373 resource.load();
374
375
376
377
378 Collections.reverse(processList);
379 deleteDataAll(dbName, schema, processList, srcdir, resource);
380
381
382 Collections.reverse(processList);
383 restoreDataAll(dbName, schema, processList, srcdir, resource);
384
385 }
386
387 public Connection getConnection(String domainName) throws SQLException {
388 String databaseName = getDataBaseName(domainName);
389 if (databaseName != null) {
390 return multiDataSource.getConnection(databaseName);
391 } else {
392 return multiDataSource.getBaseConnection();
393 }
394 }
395
396 public void insertOnDelete(String dbName, String schema, String table, DatafileLoader loader, String[] columns, String[] types)
397 throws TgwServiceException{
398 delete(dbName, schema,table);
399 insert(dbName, schema,table,loader,columns,types);
400 }
401
402 public void insert(String dbName, String schema, String table, DatafileLoader loader, String[] columns, String[] types)
403 throws TgwServiceException{
404
405 String[] data;
406 List bindDataList = new ArrayList();
407
408 while((data =loader.load()) != null){
409 Object[] obj = DatabaseUtils.bindArgs(data,types);
410 bindDataList.add(obj);
411 }
412
413 String sql = DatabaseUtils.buildRestoreQuery(schema, table.toUpperCase(), columns);
414 Class[] argTypes = convertTypes(types);
415 Collections.reverse(bindDataList);
416
417 log_.info("Starting restore contents of " + table + " " + sql);
418
419 DynaBatchHandler handler = multiDataSource.getDynaBatchHandler(dbName);
420
421 try{
422
423 handler.setSql(sql);
424 int num = handler.execute(bindDataList, argTypes);
425 log_.info(num + " rows inserted.");
426 }catch(SQLRuntimeException e){
427 throw new TgwServiceException(e);
428 }
429 }
430
431 public void delete(String dbName, String schema, String table) throws TgwServiceException{
432
433 String sql = DatabaseUtils.buildCleanQuery(schema, table.toUpperCase());
434 DynaBatchHandler handler = multiDataSource.getDynaBatchHandler("");
435
436 log_.info("Starting cleanup contents of " + table + " " + sql);
437 try{
438
439
440 handler.setSql(sql);
441 handler.execute();
442 }catch(SQLRuntimeException e){
443 throw new TgwServiceException(e);
444 }
445 }
446
447
448 private void putDataBaseName(String domainName, String databaseName){
449 databaseMap.put(domainName, databaseName);
450 }
451
452 private void removeDataBaseName(String domainName){
453 databaseMap.remove(domainName);
454 }
455
456 private String getDataBaseName(String domainName){
457 return (String)databaseMap.get(domainName);
458 }
459
460 private void backupTable(String dbName, String schema, String table,
461 DiconResource resource, String targetdir) {
462
463 Map options = new HashMap();
464
465 String uppercaseTableName = table.toUpperCase();
466 options.put(DiconResource.OPTION_NAME, uppercaseTableName);
467
468 String sql = DatabaseUtils.buildBackupQuery(schema, table);
469 DynaSelectHandler handler = multiDataSource.getDynaSelectHandler(dbName);
470
471
472 handler.setSql(sql);
473
474 List result = (List) handler.execute(null);
475
476
477 if (result.size() == 0) {
478 log_.info(table + " is empty, skip to backup.");
479 return;
480 }
481
482 String path = TgwPathResolver.getTableBackupFilePath(targetdir, uppercaseTableName);
483
484 String[] names = null;
485 StringBuffer buf = new StringBuffer();
486 DynaPropertyWrapper propwrapper = new DynaPropertyWrapper();
487
488 for (Iterator rows = result.iterator(); rows.hasNext();) {
489 DynaBean row = (DynaBean) rows.next();
490
491 if (names == null) {
492 DynaProperty[] props = row.getDynaClass().getDynaProperties();
493 propwrapper.put(props);
494 names = propwrapper.getNames();
495 resource.addComponent(propwrapper, options);
496 }
497 backupColumns(buf, row, names);
498 }
499 ResourceUtils.writeContent(path, buf.toString());
500 }
501
502 private void backupColumns(StringBuffer buf, DynaBean row, String[] names) {
503 boolean firstColumn = true;
504 for (int j = 0; j < names.length; j++) {
505
506 if (firstColumn) {
507 firstColumn = false;
508 } else {
509 buf.append(",");
510 }
511
512 Object o = row.get(names[j]);
513 if (o != null) {
514 String s = o.toString().replaceAll("\"", "\"\"");
515 buf.append("\"" + s + "\"");
516 } else {
517 buf.append("\"\"");
518 }
519
520 }
521 buf.append(Constants.LINEBREAK_CODE);
522 }
523
524 private void deleteDataAll(String dbName, String schema, List processList, String srcdir,
525 DiconResource resource) throws TgwServiceException{
526 log_.info("delete order " + processList);
527
528 for (Iterator i = processList.iterator(); i.hasNext();) {
529 String table = (String) i.next();
530 delete(dbName, schema,table);
531 }
532 }
533
534 private void restoreDataAll(String dbName, String schema, List processList, String srcdir,
535 DiconResource resource) throws TgwServiceException{
536 log_.info("restoring order " + processList);
537 for (Iterator i = processList.iterator(); i.hasNext();) {
538 String table = (String) i.next();
539 table = table.toUpperCase();
540 String path = TgwPathResolver.getTableBackupFilePath(srcdir, table);
541
542 if (ResourceUtils.isExist(path)) {
543 DynaPropertyWrapper wrapper = (DynaPropertyWrapper) resource.getComponent(table);
544 String[] types = wrapper.getTypes();
545 String[] columns = wrapper.getNames();
546 InputStream is = ResourceUtils.readContentAsStream(path);
547 DatafileLoader loader = new CSVfileLoader(is);
548 insert(dbName, schema,table,loader,columns,types);
549 loader.close();
550 }
551 }
552 }
553
554 private Class[] convertTypes(String[] types){
555
556 if(types == null){
557 return null;
558 }
559 Class[] argTypes = new Class[types.length];
560 for(int i=0; i<types.length;i++){
561 String className = types[i];
562 if("java.sql.Date".equals(className) || "java.sql.Timestamp".equals(className)){
563 className = "java.util.Date";
564 }
565 try{
566 argTypes[i] = Class.forName(className);
567 }catch(ClassNotFoundException cnfe){
568 argTypes[i] = Object.class;
569 }
570 }
571 return argTypes;
572 }
573
574 }