1 package de.juplo.plugins.hibernate4;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import com.pyx4j.log4j.MavenLogAppender;
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.ObjectInputStream;
27 import java.io.ObjectOutputStream;
28 import java.math.BigInteger;
29 import java.net.URL;
30 import java.net.URLClassLoader;
31 import java.security.MessageDigest;
32 import java.security.NoSuchAlgorithmException;
33 import java.sql.Connection;
34 import java.sql.Driver;
35 import java.sql.DriverManager;
36 import java.sql.DriverPropertyInfo;
37 import java.sql.SQLException;
38 import java.sql.SQLFeatureNotSupportedException;
39 import java.util.Comparator;
40 import java.util.Enumeration;
41 import java.util.HashMap;
42 import java.util.HashSet;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Map.Entry;
46 import java.util.Properties;
47 import java.util.Set;
48 import java.util.TreeSet;
49 import java.util.logging.Logger;
50 import java.util.regex.Matcher;
51 import java.util.regex.Pattern;
52 import javax.persistence.Embeddable;
53 import javax.persistence.Entity;
54 import javax.persistence.MappedSuperclass;
55 import org.apache.maven.artifact.Artifact;
56 import org.apache.maven.model.Resource;
57 import org.apache.maven.plugin.AbstractMojo;
58 import org.apache.maven.plugin.MojoExecutionException;
59 import org.apache.maven.plugin.MojoFailureException;
60 import org.apache.maven.project.MavenProject;
61 import org.hibernate.cfg.NamingStrategy;
62 import org.hibernate.envers.configuration.spi.AuditConfiguration;
63 import org.hibernate.tool.hbm2ddl.SchemaExport;
64 import org.hibernate.tool.hbm2ddl.SchemaExport.Type;
65 import org.hibernate.tool.hbm2ddl.Target;
66 import org.scannotation.AnnotationDB;
67
68
69
70
71
72
73
74
75
76
77
78 public class Hbm2DdlMojo extends AbstractMojo
79 {
80 public final static String EXPORT_SKIPPED_PROPERTY = "hibernate.export.skipped";
81
82 public final static String DRIVER_CLASS = "hibernate.connection.driver_class";
83 public final static String URL = "hibernate.connection.url";
84 public final static String USERNAME = "hibernate.connection.username";
85 public final static String PASSWORD = "hibernate.connection.password";
86 public final static String DIALECT = "hibernate.dialect";
87 public final static String NAMING_STRATEGY="hibernate.ejb.naming_strategy";
88 public final static String ENVERS = "hibernate.export.envers";
89
90 public final static String MD5S = "hibernate4-generatedschema.md5s";
91
92 private final static Pattern split = Pattern.compile("[^,\\s]+");
93
94
95
96
97
98
99
100
101
102
103
104 private MavenProject project;
105
106
107
108
109
110
111
112
113
114
115 private String buildDirectory;
116
117
118
119
120
121
122
123
124
125
126 private String outputDirectory;
127
128
129
130
131
132
133
134
135
136
137 private boolean scanTestClasses;
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 private String scanDependencies;
157
158
159
160
161
162
163
164
165
166
167
168
169
170 private String testOutputDirectory;
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186 private boolean skip;
187
188
189
190
191
192
193
194
195
196
197
198
199 private boolean force;
200
201
202
203
204
205
206
207 private String driverClassName;
208
209
210
211
212
213
214
215 private String url;
216
217
218
219
220
221
222
223 private String username;
224
225
226
227
228
229
230
231 private String password;
232
233
234
235
236
237
238
239 private String hibernateDialect;
240
241
242
243
244
245
246
247 private String hibernateNamingStrategy;
248
249
250
251
252
253
254
255 private String hibernateProperties;
256
257
258
259
260
261
262
263
264 private String hibernateMapping;
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281 private String target;
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297 private String type;
298
299
300
301
302
303
304
305 private String outputFile;
306
307
308
309
310
311
312
313 private String delimiter;
314
315
316
317
318
319
320
321 private boolean format;
322
323
324
325
326
327
328
329 private boolean envers;
330
331
332 @Override
333 public void execute()
334 throws
335 MojoFailureException,
336 MojoExecutionException
337 {
338 if (skip)
339 {
340 getLog().info("Execution of hibernate4-maven-plugin:export was skipped!");
341 project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true");
342 return;
343 }
344
345 Map<String,String> md5s;
346 boolean modified = false;
347 File saved = new File(buildDirectory + File.separator + MD5S);
348
349 if (saved.isFile() && saved.length() > 0)
350 {
351 try
352 {
353 FileInputStream fis = new FileInputStream(saved);
354 ObjectInputStream ois = new ObjectInputStream(fis);
355 md5s = (HashMap<String,String>)ois.readObject();
356 ois.close();
357 }
358 catch (Exception e)
359 {
360 md5s = new HashMap<String,String>();
361 getLog().warn("Cannot read timestamps from saved: " + e);
362 }
363 }
364 else
365 {
366 md5s = new HashMap<String,String>();
367 try
368 {
369 saved.createNewFile();
370 }
371 catch (IOException e)
372 {
373 getLog().debug("Cannot create file \"" + saved.getPath() + "\" for timestamps: " + e);
374 }
375 }
376
377 ClassLoader classLoader = null;
378 try
379 {
380 getLog().debug("Creating ClassLoader for project-dependencies...");
381 List<String> classpathFiles = project.getCompileClasspathElements();
382 if (scanTestClasses)
383 classpathFiles.addAll(project.getTestClasspathElements());
384 URL[] urls = new URL[classpathFiles.size()];
385 for (int i = 0; i < classpathFiles.size(); ++i)
386 {
387 getLog().debug("Dependency: " + classpathFiles.get(i));
388 urls[i] = new File(classpathFiles.get(i)).toURI().toURL();
389 }
390 classLoader = new URLClassLoader(urls, getClass().getClassLoader());
391 }
392 catch (Exception e)
393 {
394 getLog().error("Error while creating ClassLoader!", e);
395 throw new MojoExecutionException(e.getMessage());
396 }
397
398 Set<Class<?>> classes =
399 new TreeSet<Class<?>>(
400 new Comparator<Class<?>>() {
401 @Override
402 public int compare(Class<?> a, Class<?> b)
403 {
404 return a.getName().compareTo(b.getName());
405 }
406 }
407 );
408
409 try
410 {
411 AnnotationDB db = new AnnotationDB();
412 File dir = new File(outputDirectory);
413 if (dir.exists())
414 {
415 getLog().info("Scanning directory " + outputDirectory + " for annotated classes...");
416 URL dirUrl = dir.toURI().toURL();
417 db.scanArchives(dirUrl);
418 }
419 if (scanTestClasses)
420 {
421 dir = new File(testOutputDirectory);
422 if (dir.exists())
423 {
424 getLog().info("Scanning directory " + testOutputDirectory + " for annotated classes...");
425 URL dirUrl = dir.toURI().toURL();
426 db.scanArchives(dirUrl);
427 }
428 }
429 if (scanDependencies != null)
430 {
431 Matcher matcher = split.matcher(scanDependencies);
432 while (matcher.find())
433 {
434 getLog().info("Scanning dependencies for scope " + matcher.group());
435 for (Artifact artifact : project.getDependencyArtifacts())
436 {
437 if (!artifact.getScope().equalsIgnoreCase(matcher.group()))
438 continue;
439 if (artifact.getFile() == null)
440 {
441 getLog().warn(
442 "Cannot scan dependency " +
443 artifact.getId() +
444 ": no JAR-file available!"
445 );
446 continue;
447 }
448 getLog().info(
449 "Scanning dependency " +
450 artifact.getId() +
451 " for annotated classes..."
452 );
453 db.scanArchives(artifact.getFile().toURI().toURL());
454 }
455 }
456 }
457
458 Set<String> classNames = new HashSet<String>();
459 if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
460 classNames.addAll(db.getAnnotationIndex().get(Entity.class.getName()));
461 if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName()))
462 classNames.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName()));
463 if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
464 classNames.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
465
466 MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
467 for (String name : classNames)
468 {
469 Class<?> annotatedClass = classLoader.loadClass(name);
470 classes.add(annotatedClass);
471 String resourceName = annotatedClass.getName();
472 resourceName = resourceName.substring(resourceName.lastIndexOf(".") + 1, resourceName.length()) + ".class";
473 InputStream is =
474 annotatedClass
475 .getResourceAsStream(resourceName);
476 byte[] buffer = new byte[1024*4];
477 int i;
478 while((i = is.read(buffer)) > -1)
479 digest.update(buffer, 0, i);
480 is.close();
481 byte[] bytes = digest.digest();
482 BigInteger bi = new BigInteger(1, bytes);
483 String newMd5 = String.format("%0" + (bytes.length << 1) + "x", bi);
484 String oldMd5 = !md5s.containsKey(name) ? "" : md5s.get(name);
485 if (!newMd5.equals(oldMd5))
486 {
487 getLog().debug("Found new or modified annotated class: " + name);
488 modified = true;
489 md5s.put(name, newMd5);
490 }
491 else
492 {
493 getLog().debug(oldMd5 + " -> class unchanged: " + name);
494 }
495 }
496 }
497 catch (ClassNotFoundException e)
498 {
499 getLog().error("Error while adding annotated classes!", e);
500 throw new MojoExecutionException(e.getMessage());
501 }
502 catch (Exception e)
503 {
504 getLog().error("Error while scanning!", e);
505 throw new MojoFailureException(e.getMessage());
506 }
507
508 if (classes.isEmpty())
509 {
510 if (hibernateMapping == null || hibernateMapping.isEmpty())
511 throw new MojoFailureException("No annotated classes found in directory " + outputDirectory);
512 }
513 else
514 {
515 getLog().debug("Detected classes with mapping-annotations:");
516 for (Class<?> annotatedClass : classes)
517 getLog().debug(" " + annotatedClass.getName());
518 }
519
520
521 Properties properties = new Properties();
522
523
524 try
525 {
526 File file = new File(hibernateProperties);
527 if (file.exists())
528 {
529 getLog().info("Reading properties from file " + hibernateProperties + "...");
530 properties.load(new FileInputStream(file));
531 }
532 else
533 getLog().info("No hibernate-properties-file found! (Checked path: " + hibernateProperties + ")");
534 }
535 catch (IOException e)
536 {
537 getLog().error("Error while reading properties!", e);
538 throw new MojoExecutionException(e.getMessage());
539 }
540
541
542 if (driverClassName != null)
543 {
544 if (properties.containsKey(DRIVER_CLASS))
545 getLog().debug(
546 "Overwriting property " +
547 DRIVER_CLASS + "=" + properties.getProperty(DRIVER_CLASS) +
548 " with the value " + driverClassName
549 );
550 else
551 getLog().debug("Using the value " + driverClassName);
552 properties.setProperty(DRIVER_CLASS, driverClassName);
553 }
554 if (url != null)
555 {
556 if (properties.containsKey(URL))
557 getLog().debug(
558 "Overwriting property " +
559 URL + "=" + properties.getProperty(URL) +
560 " with the value " + url
561 );
562 else
563 getLog().debug("Using the value " + url);
564 properties.setProperty(URL, url);
565 }
566 if (username != null)
567 {
568 if (properties.containsKey(USERNAME))
569 getLog().debug(
570 "Overwriting property " +
571 USERNAME + "=" + properties.getProperty(USERNAME) +
572 " with the value " + username
573 );
574 else
575 getLog().debug("Using the value " + username);
576 properties.setProperty(USERNAME, username);
577 }
578 if (password != null)
579 {
580 if (properties.containsKey(PASSWORD))
581 getLog().debug(
582 "Overwriting property " +
583 PASSWORD + "=" + properties.getProperty(PASSWORD) +
584 " with value " + password
585 );
586 else
587 getLog().debug("Using value " + password + " for property " + PASSWORD);
588 properties.setProperty(PASSWORD, password);
589 }
590 if (hibernateDialect != null)
591 {
592 if (properties.containsKey(DIALECT))
593 getLog().debug(
594 "Overwriting property " +
595 DIALECT + "=" + properties.getProperty(DIALECT) +
596 " with value " + hibernateDialect
597 );
598 else
599 getLog().debug(
600 "Using value " + hibernateDialect + " for property " + DIALECT
601 );
602 properties.setProperty(DIALECT, hibernateDialect);
603 }
604 else
605 {
606 hibernateDialect = properties.getProperty(DIALECT);
607 }
608 if ( hibernateNamingStrategy != null )
609 {
610 if ( properties.contains(NAMING_STRATEGY))
611 getLog().debug(
612 "Overwriting property " +
613 NAMING_STRATEGY + "=" + properties.getProperty(NAMING_STRATEGY) +
614 " with value " + hibernateNamingStrategy
615 );
616 else
617 getLog().debug(
618 "Using value " + hibernateNamingStrategy + " for property " +
619 NAMING_STRATEGY
620 );
621 properties.setProperty(NAMING_STRATEGY, hibernateNamingStrategy);
622 }
623
624
625 if (md5s.containsKey(DIALECT))
626 {
627 String dialect = properties.getProperty(DIALECT);
628 if (md5s.get(DIALECT).equals(dialect))
629 getLog().debug("SQL-dialect unchanged.");
630 else
631 {
632 modified = true;
633 if (dialect == null)
634 {
635 getLog().debug("SQL-dialect was unset.");
636 md5s.remove(DIALECT);
637 }
638 else
639 {
640 getLog().debug("SQL-dialect changed: " + dialect);
641 md5s.put(DIALECT, dialect);
642 }
643 }
644 }
645 else
646 {
647 String dialect = properties.getProperty(DIALECT);
648 if (dialect != null)
649 {
650 modified = true;
651 md5s.put(DIALECT, properties.getProperty(DIALECT));
652 }
653 }
654
655
656 if (md5s.get(ENVERS) != null)
657 {
658 if (md5s.get(ENVERS).equals(Boolean.toString(envers)))
659 getLog().debug("Envers-Configuration unchanged. Enabled: " + envers);
660 else
661 {
662 getLog().debug("Envers-Configuration changed. Enabled: " + envers);
663 modified = true;
664 md5s.put(ENVERS, Boolean.toString(envers));
665 }
666 }
667 else
668 {
669 modified = true;
670 md5s.put(ENVERS, Boolean.toString(envers));
671 }
672
673 if (properties.isEmpty())
674 {
675 getLog().error("No properties set!");
676 throw new MojoFailureException("Hibernate configuration is missing!");
677 }
678
679 final ValidationConfiguration config = new ValidationConfiguration(hibernateDialect);
680
681 config.setProperties(properties);
682
683 if ( properties.containsKey(NAMING_STRATEGY))
684 {
685 String namingStrategy = properties.getProperty(NAMING_STRATEGY);
686 getLog().debug("Explicitly set NamingStrategy: " + namingStrategy);
687 try
688 {
689 @SuppressWarnings("unchecked")
690 Class<NamingStrategy> namingStrategyClass = (Class<NamingStrategy>) Class.forName(namingStrategy);
691 config.setNamingStrategy(namingStrategyClass.newInstance());
692 }
693 catch (Exception e)
694 {
695 getLog().error("Error setting NamingStrategy", e);
696 throw new MojoExecutionException(e.getMessage());
697 }
698 }
699
700 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
701 Connection connection = null;
702 MavenLogAppender.startPluginLog(this);
703 try
704 {
705
706
707
708
709 Thread.currentThread().setContextClassLoader(classLoader);
710
711 getLog().debug("Adding annotated classes to hibernate-mapping-configuration...");
712
713 Set<String> packages = new HashSet<String>();
714 for (Class<?> annotatedClass : classes)
715 {
716 String packageName = annotatedClass.getPackage().getName();
717 if (!packages.contains(packageName))
718 {
719 getLog().debug("Add package " + packageName);
720 packages.add(packageName);
721 config.addPackage(packageName);
722 getLog().debug("type definintions" + config.getTypeDefs());
723 }
724 getLog().debug("Class " + annotatedClass);
725 config.addAnnotatedClass(annotatedClass);
726 }
727
728 if (hibernateMapping != null)
729 {
730 try
731 {
732 MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
733 for (String filename : hibernateMapping.split("[\\s,]+"))
734 {
735
736 File file = new File(filename);
737 if (!file.exists())
738 {
739
740 for (Resource resource : project.getResources())
741 {
742 file = new File(resource.getDirectory() + File.separator + filename);
743 if (file.exists())
744 break;
745 }
746 }
747 if (file != null && file.exists())
748 {
749 InputStream is = new FileInputStream(file);
750 byte[] buffer = new byte[1024*4];
751 int i;
752 while((i = is.read(buffer)) > -1)
753 digest.update(buffer, 0, i);
754 is.close();
755 byte[] bytes = digest.digest();
756 BigInteger bi = new BigInteger(1, bytes);
757 String newMd5 = String.format("%0" + (bytes.length << 1) + "x", bi);
758 String oldMd5 = !md5s.containsKey(filename) ? "" : md5s.get(filename);
759 if (!newMd5.equals(oldMd5))
760 {
761 getLog().debug("Found new or modified mapping-file: " + filename);
762 modified = true;
763 md5s.put(filename, newMd5);
764 }
765 else
766 {
767 getLog().debug(oldMd5 + " -> mapping-file unchanged: " + filename);
768 }
769 getLog().debug("Adding mappings from XML-configurationfile: " + file);
770 config.addFile(file);
771 }
772 else
773 throw new MojoFailureException("File " + filename + " could not be found in any of the configured resource-directories!");
774 }
775 }
776 catch (NoSuchAlgorithmException e)
777 {
778 throw new MojoFailureException("Cannot calculate MD5 sums!", e);
779 }
780 catch (FileNotFoundException e)
781 {
782 throw new MojoFailureException("Cannot calculate MD5 sums!", e);
783 }
784 catch (IOException e)
785 {
786 throw new MojoFailureException("Cannot calculate MD5 sums!", e);
787 }
788 }
789
790 Target target = null;
791 try
792 {
793 target = Target.valueOf(this.target.toUpperCase());
794 }
795 catch (IllegalArgumentException e)
796 {
797 getLog().error("Invalid value for configuration-option \"target\": " + this.target);
798 getLog().error("Valid values are: NONE, SCRIPT, EXPORT, BOTH");
799 throw new MojoExecutionException("Invalid value for configuration-option \"target\"");
800 }
801 Type type = null;
802 try
803 {
804 type = Type.valueOf(this.type.toUpperCase());
805 }
806 catch (IllegalArgumentException e)
807 {
808 getLog().error("Invalid value for configuration-option \"type\": " + this.type);
809 getLog().error("Valid values are: NONE, CREATE, DROP, BOTH");
810 throw new MojoExecutionException("Invalid value for configuration-option \"type\"");
811 }
812
813 if (target.equals(Target.SCRIPT) || target.equals(Target.NONE))
814 {
815 project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true");
816 }
817 if (
818 !modified
819 && !target.equals(Target.SCRIPT)
820 && !target.equals(Target.NONE)
821 && !force
822 )
823 {
824 getLog().info("No modified annotated classes or mapping-files found and dialect unchanged.");
825 getLog().info("Skipping schema generation!");
826 project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true");
827 return;
828 }
829
830 getLog().info("Gathered hibernate-configuration (turn on debugging for details):");
831 for (Entry<Object,Object> entry : properties.entrySet())
832 getLog().info(" " + entry.getKey() + " = " + entry.getValue());
833
834 try
835 {
836
837
838
839
840
841 getLog().debug("Target: " + target + ", Type: " + type);
842 switch (target)
843 {
844 case EXPORT:
845 case BOTH:
846 switch (type)
847 {
848 case CREATE:
849 case DROP:
850 case BOTH:
851 Class driverClass = classLoader.loadClass(properties.getProperty(DRIVER_CLASS));
852 getLog().debug("Registering JDBC-driver " + driverClass.getName());
853 DriverManager.registerDriver(new DriverProxy((Driver)driverClass.newInstance()));
854 getLog().debug(
855 "Opening JDBC-connection to "
856 + properties.getProperty(URL)
857 + " as "
858 + properties.getProperty(USERNAME)
859 + " with password "
860 + properties.getProperty(PASSWORD)
861 );
862 connection = DriverManager.getConnection(
863 properties.getProperty(URL),
864 properties.getProperty(USERNAME),
865 properties.getProperty(PASSWORD)
866 );
867 }
868 }
869 }
870 catch (ClassNotFoundException e)
871 {
872 getLog().error("Dependency for driver-class " + properties.getProperty(DRIVER_CLASS) + " is missing!");
873 throw new MojoExecutionException(e.getMessage());
874 }
875 catch (Exception e)
876 {
877 getLog().error("Cannot establish connection to database!");
878 Enumeration<Driver> drivers = DriverManager.getDrivers();
879 if (!drivers.hasMoreElements())
880 getLog().error("No drivers registered!");
881 while (drivers.hasMoreElements())
882 getLog().debug("Driver: " + drivers.nextElement());
883 throw new MojoExecutionException(e.getMessage());
884 }
885
886 config.buildMappings();
887
888 if (envers)
889 {
890 getLog().info("Automatic auditing via hibernate-envers enabled!");
891 AuditConfiguration.getFor(config);
892 }
893
894 SchemaExport export = new SchemaExport(config, connection);
895 export.setDelimiter(delimiter);
896 export.setFormat(format);
897
898 File outF = new File(outputFile);
899
900 if (!outF.isAbsolute())
901 {
902
903 outF = new File(buildDirectory, outputFile);
904 getLog().info("Adjusted relative path, resulting path is " + outF.getPath());
905 }
906
907
908 File outFileParentDir = outF.getParentFile();
909 if (null != outFileParentDir && !outFileParentDir.exists())
910 {
911 try
912 {
913 getLog().info("Creating directory path for output file:" + outFileParentDir.getPath());
914 outFileParentDir.mkdirs();
915 }
916 catch (Exception e)
917 {
918 getLog().error("Error creating directory path for output file: " + e.getLocalizedMessage());
919 }
920 }
921
922 export.setOutputFile(outF.getPath());
923 export.execute(target, type);
924
925 for (Object exception : export.getExceptions())
926 getLog().debug(exception.toString());
927 }
928 finally
929 {
930
931 MavenLogAppender.endPluginLog(this);
932
933
934 Thread.currentThread().setContextClassLoader(contextClassLoader);
935
936
937 try
938 {
939 if (connection != null)
940 connection.close();
941 }
942 catch (SQLException e)
943 {
944 getLog().error("Error while closing connection: " + e.getMessage());
945 }
946 }
947
948
949 try
950 {
951 FileOutputStream fos = new FileOutputStream(saved);
952 ObjectOutputStream oos = new ObjectOutputStream(fos);
953 oos.writeObject(md5s);
954 oos.close();
955 fos.close();
956 }
957 catch (Exception e)
958 {
959 getLog().error("Cannot write md5-sums to file: " + e);
960 }
961 }
962
963
964
965
966
967
968
969 static final class DriverProxy implements Driver
970 {
971 private final Driver target;
972
973 DriverProxy(Driver target)
974 {
975 if (target == null)
976 throw new NullPointerException();
977 this.target = target;
978 }
979
980 public java.sql.Driver getTarget()
981 {
982 return target;
983 }
984
985 @Override
986 public boolean acceptsURL(String url) throws SQLException
987 {
988 return target.acceptsURL(url);
989 }
990
991 @Override
992 public java.sql.Connection connect(
993 String url,
994 java.util.Properties info
995 )
996 throws
997 SQLException
998 {
999 return target.connect(url, info);
1000 }
1001
1002 @Override
1003 public int getMajorVersion()
1004 {
1005 return target.getMajorVersion();
1006 }
1007
1008 @Override
1009 public int getMinorVersion()
1010 {
1011 return target.getMinorVersion();
1012 }
1013
1014 @Override
1015 public DriverPropertyInfo[] getPropertyInfo(
1016 String url,
1017 Properties info
1018 )
1019 throws
1020 SQLException
1021 {
1022 return target.getPropertyInfo(url, info);
1023 }
1024
1025 @Override
1026 public boolean jdbcCompliant()
1027 {
1028 return target.jdbcCompliant();
1029 }
1030
1031
1032
1033
1034
1035 public Logger getParentLogger() throws SQLFeatureNotSupportedException
1036 {
1037 throw new SQLFeatureNotSupportedException("Not supported, for backward-compatibility with Java 1.6");
1038 }
1039
1040 @Override
1041 public String toString()
1042 {
1043 return "Proxy: " + target;
1044 }
1045
1046 @Override
1047 public int hashCode()
1048 {
1049 return target.hashCode();
1050 }
1051
1052 @Override
1053 public boolean equals(Object obj)
1054 {
1055 if (!(obj instanceof DriverProxy))
1056 return false;
1057 DriverProxy other = (DriverProxy) obj;
1058 return this.target.equals(other.target);
1059 }
1060 }
1061 }