1   package com.trendmicro.grid.acl.mssql;
2   
3   import net.sf.tinyjee.config.Configuration;
4   import net.sf.tinyjee.config.ConfigurationVisitor;
5   import net.sf.tinyjee.config.Connection;
6   import net.sf.tinyjee.config.PropertySection;
7   import net.sf.tinyjee.config.documentation.PropertyDocumentation;
8   import net.sf.tinyjee.config.documentation.ResourceDocumentation;
9   import net.sf.tinyjee.config.documentation.SectionDocumentation;
10  import net.sf.tinyjee.ds.mssql.MSSQLConfigurationBuilder;
11  import org.slf4j.Logger;
12  import org.slf4j.LoggerFactory;
13  
14  import java.util.Collection;
15  
16  import static net.sf.tinyjee.config.Connection.Protocol.jdbc;
17  import static net.sf.tinyjee.config.Connection.createGroupFilter;
18  import static net.sf.tinyjee.config.Connection.createProtocolFilter;
19  import static net.sf.tinyjee.config.Connection.select;
20  import static net.sf.tinyjee.config.Names.*;
21  import static net.sf.tinyjee.ds.config.DataSourceConnection.*;
22  
23  /**
24   * Configures the MSSQL Datasource inside the main configuration file.
25   *
26   * @author Juergen_Kellerer, 2010-06-12
27   */
28  @ResourceDocumentation(
29          name = MSSQLConfigurator.NAME_CONNECTION,
30          description = "Defines an outing JDBC connection to the MSSQL database used to store the CoreDB schema.\n" +
31                  "\n" +
32                  "This connection is only active if the system property 'mssqlserver' is set to " +
33                  "the server's 'hostname'. The access layer will gracefully fallback to using a local " +
34                  "HSQL database if no MSSQL server is specified.\n" +
35                  "\n" +
36                  "Use the command line option '-Dmssqlserver=hostname' to let the access layer use " +
37                  "the MSSQL server instead of its own embedded database.\n" +
38                  "\n" +
39                  "For high-availability use '-DmssqlserverFailover=hostname' to specify an alternate " +
40                  "failover partner server to use when the primary MSSQL server is shutdown.\n" +
41                  "\n" +
42                  "Note: In some cases it may be desirable to harden the MSSQL host configuration.\n" +
43                  "In order to achieve this, remove the 'if' attribute from the connection tag and " +
44                  "replace '${mssqlserver}' inside the connection address with the hardened host value. " +
45                  "After this change, setting the command line option -Dmssqlserver will no longer " +
46                  "have any effect.",
47          properties = {
48                  @PropertyDocumentation(property = MSSQLConfigurator.CONFIGURATION_VERSION,
49                          description = "This is a revision number for the auto-configurator.\n" +
50                                  "Do not modify this."),
51                  @PropertyDocumentation(property = MSSQLConfigurator.CONFIGURATION_USE_XA,
52                          description = "This is a state information for the auto-configurator.\n" +
53                                  "Do not modify this.\n" +
54                                  "\n" +
55                                  "Use '-Dmssql.no.xa=true' to enable emulated-XA instead of real XA.")
56          }
57  )
58  @SectionDocumentation(
59          name = "tinyjee-jpa",
60          properties = {
61                  @PropertyDocumentation(
62                          property = "jpaDataSource.CoreDB",
63                          description = "Sets the name of the datasource to use for accessing the GRID CoreDB schema. " +
64                                  "By default, an expression is used that maps the MSSQL datasource if available " +
65                                  "and uses the embedded HSQL database as a fallback. It is not recommended to " +
66                                  "change this default value."
67                  )
68          }
69  )
70  public class MSSQLConfigurator implements ConfigurationVisitor, MSSQLSystemProperties {
71  
72      private static final Logger log = LoggerFactory.getLogger(MSSQLConfigurator.class);
73  
74      public static final String NAME_CONNECTION = "gacl-mssql";
75  
76      public static final String CONFIGURATION_VERSION = "auto-configured-version";
77      public static final int CONFIGURATION_VERSION_VALUE = 2;
78  
79      public static final String CONFIGURATION_USE_XA = "auto-configured-useXA";
80  
81      /**
82       * Indicates whether the MSSQL driver is used with emulated instead of full XA support.
83       */
84      public static final boolean MSSQL_NO_XA = Boolean.getBoolean(PROPERTY_NO_XA);
85  
86      public static final boolean MSSQL_USE_AO = Boolean.getBoolean(PROPERTY_USE_AO);
87  
88      private static final String DEFAULT_DB_NAME = "GRIDCoreDB";
89      private static final String DEFAULT_USER_NAME = "CoreDB";
90      private static final String DEFAULT_PASSWORD = "tr3ndm1cro";
91  
92      @Override
93      public void visitConfiguration(Configuration configuration) {
94          configureJpaSection(configuration);
95          configureMSSQLConnections(configuration);
96      }
97  
98      private void configureMSSQLConnections(Configuration configuration) {
99          final String group = MSSQLConfigurationBuilder.GROUP;
100         Collection<Connection> connections = select(configuration.getConnections(), createProtocolFilter(jdbc), createGroupFilter(group));
101 
102         if (connections.isEmpty()) {
103             log.warn("TMACL-01580:No MSSQL connections were defined. MSSQL support is disabled.");
104         } else {
105             for (Connection connection : connections) {
106                 if (connection.getPropertyValue(CONFIGURATION_VERSION, 0D) >= CONFIGURATION_VERSION_VALUE &&
107                         connection.getPropertyValue(CONFIGURATION_USE_XA, true) != MSSQL_NO_XA) {
108                     if (log.isTraceEnabled()) {
109                         log.trace("Not adjusting configuration for {} as XA state is not changed and revision number is not " +
110                                 "smaller than expected.", connection);
111                     }
112                     continue;
113                 }
114 
115                 connection.setName(NAME_CONNECTION);
116                 connection.setIfRawValue("${not empty " + PROPERTY_HOST + "}");
117 
118                 // Host and Port configuration
119                 connection.setAddress("${" + PROPERTY_HOST + "}");
120                 if (connection.getPortNumber() == -1) connection.getPort().setRawValue("1433");
121                 connection.setProperty(DATABASE_NAME, DEFAULT_DB_NAME);
122 
123                 // Driver setup
124                 connection.setProperty(CONFIGURATION_USE_XA, "").setValue(!MSSQL_NO_XA);
125                 if (MSSQL_NO_XA) {
126                     // Cleanup
127                     connection.removeProperty("serverName");
128                     connection.removeProperty("failoverPartner");
129                     connection.removeProperty("portNumber");
130 
131                     // Configure connection with emulated XA datasource
132                     connection.setProperty(DATASOURCE_CLASS, "bitronix.tm.resource.jdbc.lrc.LrcXADataSource");
133                     if (!MSSQL_USE_AO)
134                         connection.setProperty("url", "jdbc:sqlserver://${" + self("address") + "}:${" + self("port") + "};" +
135                                 "databaseName=" + DEFAULT_DB_NAME + ";failoverPartner=${" + PROPERTY_FAILOVER_HOST + '}');
136                     else
137                         connection.setProperty("url", "jdbc:sqlserver://${" + self("address") + "}:${" + self("port") + "};" +
138                                 "databaseName=" + DEFAULT_DB_NAME + ";multiSubnetFailover=true;applicationIntent=${" + PROPERTY_AO_READONLY + '}');
139                     connection.setProperty("driverClassName", "com.microsoft.sqlserver.jdbc.SQLServerDriver");
140                 } else {
141                     // Cleanup
142                     connection.removeProperty("url");
143                     connection.removeProperty("driverClassName");
144 
145                     // Configure connection with true XA datasource
146                     connection.setProperty(DATASOURCE_CLASS, "com.microsoft.sqlserver.jdbc.SQLServerXADataSource");
147 
148                     connection.setProperty("serverName", "${" + self("address") + '}');
149                     if (!MSSQL_USE_AO)
150                         connection.setProperty("failoverPartner", "${" + PROPERTY_FAILOVER_HOST + '}');
151                     else {
152                         connection.setProperty("multiSubnetFailover", "true");
153                         connection.setProperty("applicationIntent", PROPERTY_AO_READONLY);
154                     }
155 
156                     connection.setProperty("portNumber", "${" + self("port") + '}');
157                 }
158 
159                 // Connection pool & TX manager configuration
160                 connection.setProperty(MAX_IDLE_TIME, "10");
161                 connection.setProperty("ignoreRecoveryFailures", "true");
162                 connection.setProperty("enableJdbc4ConnectionTest", "true");
163 
164                 // Credentials (set only if missing to avoid that we overwrite manually adjusted properties)
165                 if ("sa".equals(connection.getPropertyValue(USER, "sa"))) {
166                     connection.setProperty(USER, DEFAULT_USER_NAME);
167                     connection.setProperty(PASSWORD, DEFAULT_PASSWORD);
168                 }
169 
170                 // JPA/Hibernate related setup
171                 connection.setProperty(JpaDatabaseDialect.key(), MSSQL2008Dialect.class.getName());
172                 connection.setProperty(JpaSchemaCreationPolicy.key(), "validate");
173 
174                 // Marker value to allow upgrading existing configuration
175                 connection.setProperty(CONFIGURATION_VERSION, String.valueOf(CONFIGURATION_VERSION_VALUE));
176             }
177         }
178     }
179 
180     private static String self(String property) {
181         return "connections['" + NAME_CONNECTION + "']['" + property + "']";
182     }
183 
184     private void configureJpaSection(Configuration configuration) {
185         PropertySection section = configuration.getPropertySection(JpaSectionTitle.key());
186         if (section != null) {
187             String propertyName = JpaDataSourcePrefix.key() + "CoreDB";
188             String value = "java:${" + self("enabled") + " ? '" + NAME_CONNECTION + "' : 'hsql'}";
189             section.setProperty(propertyName, value);
190         } else {
191             log.warn("TMACL-00960:JPA cannot be configured with MSSQL support, " +
192                     "the relevant configuration section is missing (JPA module not in classpath?).");
193         }
194     }
195 }
196 
197