Author: mgentry Date: Mon Dec 18 08:47:02 2006 New Revision: 488331 URL: http://svn.apache.org/viewvc?view=rev&rev=488331 Log: Modifications to support the CAY-642 enhancement to allow database passwords to be encoded differently.
Added: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/PasswordEncoding.java incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/PlainTextPasswordEncoder.java incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/Rot13PasswordEncoder.java incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/Rot47PasswordEncoder.java Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/ConfigSaver.java incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/DriverDataSourceFactory.java incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conn/DataSourceInfo.java incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/project/DataNodeFile.java incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/JDBCDataSourceEditor.java incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/JDBCDataSourceView.java incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/MainDataNodeEditor.java Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/ConfigSaver.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/ConfigSaver.java?view=diff&rev=488331&r1=488330&r2=488331 ============================================================================== --- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/ConfigSaver.java (original) +++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/ConfigSaver.java Mon Dec 18 08:47:02 2006 @@ -20,6 +20,10 @@ package org.apache.cayenne.conf; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; import java.io.PrintWriter; import java.util.Iterator; @@ -154,43 +158,69 @@ pw.println("</domain>"); } + private String attribute(String key, String value) + { + if (value != null) + return " " + key + "=\"" + Util.encodeXmlAttribute(value) + "\""; + else + return ""; + } + /** * Stores DataSolurceInfo to the specified PrintWriter. * <code>info</code> object may contain full or partial information. */ - public void storeDataNode(PrintWriter out, DataSourceInfo info) { - out.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); - out.print("<driver project-version=\"" + Project.CURRENT_PROJECT_VERSION + "\""); - if (info.getJdbcDriver() != null) { - out.print(" class=\"" + info.getJdbcDriver() + "\""); - } - out.println(">"); - - if (info.getDataSourceUrl() != null) { - String encoded = Util.encodeXmlAttribute(info.getDataSourceUrl()); - out.println("\t<url value=\"" + encoded + "\"/>"); - } - - out.println( - "\t<connectionPool min=\"" - + info.getMinConnections() - + "\" max=\"" - + info.getMaxConnections() - + "\" />"); - - if (info.getUserName() != null || info.getPassword() != null) { - out.print("\t<login"); - if (info.getUserName() != null) { - String encoded = Util.encodeXmlAttribute(info.getUserName()); - out.print(" userName=\"" + encoded + "\""); - } - if (info.getPassword() != null) { - String encoded = Util.encodeXmlAttribute(info.getPassword()); - out.print(" password=\"" + encoded + "\""); - } - out.println("/>"); - } + public void storeDataNode(PrintWriter pw, Project project, DataSourceInfo info) { + pw.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); + pw.println("<driver" + + attribute("project-version", Project.CURRENT_PROJECT_VERSION) + + attribute("class", info.getJdbcDriver()) + ">"); + + if (info.getDataSourceUrl() != null) + pw.println("\t<url" + attribute("value", info.getDataSourceUrl()) + "/>"); + + pw.println("\t<connectionPool" + + attribute("min", String.valueOf(info.getMinConnections())) + + attribute("max", String.valueOf(info.getMaxConnections())) + "/>"); + + if (info.getUserName() != null) { + pw.print("\t<login" + attribute("userName", info.getUserName())); + + if (info.getPasswordLocation().equals(DataSourceInfo.PASSWORD_LOCATION_MODEL)) + { + PasswordEncoding encoder = info.getPasswordEncoder(); + if (encoder != null) + pw.print(attribute("password", encoder.encodePassword(info.getPassword(), info.getPasswordEncoderSalt()))); + } + else if (info.getPasswordLocation().equals(DataSourceInfo.PASSWORD_LOCATION_CLASSPATH)) + { + if (info.getPasswordSource() != null) + { + File passwordFile = new File(project.getProjectDirectory() + File.separator + info.getPasswordSource()); + PasswordEncoding encoder = info.getPasswordEncoder(); + if (encoder != null && passwordFile != null) + { + try + { + PrintStream out = new PrintStream(new FileOutputStream(passwordFile)); + out.print(encoder.encodePassword(info.getPassword(), info.getPasswordEncoderSalt())); + out.close(); + } + catch (FileNotFoundException exception) + { + // TODO Auto-generated catch block + exception.printStackTrace(); + } + } + } + } + + pw.println(attribute("encoderClass", info.getPasswordEncoderClass()) + + attribute("encoderSalt", info.getPasswordEncoderSalt()) + + attribute("passwordLocation", info.getPasswordLocation()) + + attribute("passwordSource", info.getPasswordSource()) + "/>"); + } - out.println("</driver>"); + pw.println("</driver>"); } } Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/DriverDataSourceFactory.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/DriverDataSourceFactory.java?view=diff&rev=488331&r1=488330&r2=488331 ============================================================================== --- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/DriverDataSourceFactory.java (original) +++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/DriverDataSourceFactory.java Mon Dec 18 08:47:02 2006 @@ -19,7 +19,12 @@ package org.apache.cayenne.conf; +import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; import javax.sql.DataSource; @@ -231,10 +236,131 @@ super(parser, parentHandler); } + private String passwordFromInputStream(InputStream inputStream) + { + BufferedReader bufferedReader = null; + InputStreamReader inputStreamReader = null; + String password = null; + + try + { + inputStreamReader = new InputStreamReader(inputStream); + bufferedReader = new BufferedReader(inputStreamReader); + password = bufferedReader.readLine(); + } + catch (IOException exception) + { + exception.printStackTrace(); + } + finally + { + try + { bufferedReader.close(); } + catch (Exception exception) + { } + + try + { inputStreamReader.close(); } + catch (Exception exception) + { } + + try + { inputStream.close(); } + catch (IOException exception) + { } + } + + return password; + } + + private String passwordFromURL(URL url) + { + InputStream inputStream = null; + String password = null; + + try + { + inputStream = url.openStream(); + password = passwordFromInputStream(inputStream); + } + catch (IOException exception) + { + // Log the error while trying to open the stream. A null + // password will be returned as a result. + exception.printStackTrace(); + } + + return password; + } + public void init(String name, Attributes atts, DataSourceInfo driverInfo) { logger.info("loading user name and password."); - driverInfo.setUserName(atts.getValue("userName")); - driverInfo.setPassword(atts.getValue("password")); + + String encoderClass = atts.getValue("encoderClass"); + String encoderSalt = atts.getValue("encoderSalt"); + String password = atts.getValue("password"); + String passwordLocation = atts.getValue("passwordLocation"); + String passwordSource = atts.getValue("passwordSource"); + String username = atts.getValue("userName"); + + driverInfo.setPasswordEncoderClass(encoderClass); + driverInfo.setPasswordEncoderSalt(encoderSalt); + driverInfo.setPasswordLocation(passwordLocation); + driverInfo.setPasswordSource(passwordSource); + driverInfo.setUserName(username); + + // Replace {} in passwordSource with encoderSalt -- useful for EXECUTABLE & URL options + if (encoderSalt != null) + passwordSource = passwordSource.replace("\\{\\}", encoderSalt); + + PasswordEncoding passwordEncoder = driverInfo.getPasswordEncoder(); + + if (passwordLocation != null) // New style model (v1.2), process extra locations + { + if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_CLASSPATH)) + { + URL url = parentConfiguration.getResourceLocator().findResource(passwordSource); + + if (url != null) + password = passwordFromURL(url); + else + logger.error("Could not find resource in CLASSPATH: " + passwordSource); + } + else if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_URL)) + { + try + { + password = passwordFromURL(new URL(passwordSource)); + } + catch (MalformedURLException exception) + { + exception.printStackTrace(); + } + } + else if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_EXECUTABLE)) + { + if (passwordSource != null) + { + try + { + Process process = Runtime.getRuntime().exec(passwordSource); + password = passwordFromInputStream(process.getInputStream()); + process.waitFor(); + } + catch (IOException exception) + { + exception.printStackTrace(); + } + catch (InterruptedException exception) + { + exception.printStackTrace(); + } + } + } + } + + if (password != null && passwordEncoder != null) + driverInfo.setPassword(passwordEncoder.decodePassword(password, encoderSalt)); } } Added: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/PasswordEncoding.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/PasswordEncoding.java?view=auto&rev=488331 ============================================================================== --- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/PasswordEncoding.java (added) +++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/PasswordEncoding.java Mon Dec 18 08:47:02 2006 @@ -0,0 +1,61 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + + +package org.apache.cayenne.conf; + +/** + * Password encoders are used to translate the text of the database password, + * on loading and on saving, from one form to another. It can facilitate + * the obscuring of the password text to make database connection information + * less obvious to someone who stumbles onto the password. + * + * Cayenne only includes facilities to obscure, not encrypt, the database + * password. The mechanism is user-extensible, though, so should stronger + * security features be required, they can be added and integrated into + * both the modeler and framework. + * + * @since 3.0 + * @author Michael Gentry + */ +public interface PasswordEncoding +{ + final String[] standardEncoders = + new String[] { PlainTextPasswordEncoder.class.getName(), + Rot13PasswordEncoder.class.getName(), + Rot47PasswordEncoder.class.getName() }; + + /** + * Decodes an encoded database password. + * + * @param encodedPassword - The encoded password to be decoded + * @param salt - An optional data element which can be used to salt the algorithm. + * @return The decoded normal/plain plassword. + */ + public String decodePassword(String encodedPassword, String salt); + + /** + * Encodes a normal/plain database password. + * + * @param normalPassword - The normal/plain password to be encoded + * @param salt - An optional data element which can be used to salt the algorithm. + * @return The encoded password. + */ + public String encodePassword(String normalPassword, String salt); +} Added: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/PlainTextPasswordEncoder.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/PlainTextPasswordEncoder.java?view=auto&rev=488331 ============================================================================== --- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/PlainTextPasswordEncoder.java (added) +++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/PlainTextPasswordEncoder.java Mon Dec 18 08:47:02 2006 @@ -0,0 +1,49 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + + +package org.apache.cayenne.conf; + +/** + * The plain text password encoder passes the text of the database password + * straight-through without any alteration. This is identical to the + * behavior of pre-3.0 versions of Cayenne, where the password was stored + * in the XML model in clear text. + * + * @since 3.0 + * @author Michael Gentry + */ +public class PlainTextPasswordEncoder implements PasswordEncoding +{ + /* (non-Javadoc) + * @see org.apache.cayenne.conf.PasswordEncoding#decodePassword(java.lang.String, java.lang.String) + */ + public String decodePassword(String encodedPassword, String salt) + { + return encodedPassword; + } + + /* (non-Javadoc) + * @see org.apache.cayenne.conf.PasswordEncoding#encodePassword(java.lang.String, java.lang.String) + */ + public String encodePassword(String normalPassword, String salt) + { + return normalPassword; + } +} Added: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/Rot13PasswordEncoder.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/Rot13PasswordEncoder.java?view=auto&rev=488331 ============================================================================== --- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/Rot13PasswordEncoder.java (added) +++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/Rot13PasswordEncoder.java Mon Dec 18 08:47:02 2006 @@ -0,0 +1,98 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + + +package org.apache.cayenne.conf; + +/** + * The ROT-13 password encoder passes the text of the database password + * through a simple Caesar cipher to obscure the password text. The ROT-13 + * cipher only processes letters -- numbers and symbols are left untouched. + * ROT-13 is also a symmetrical cipher and therefore provides no real + * encryption since applying the cipher to the encrypted text produces the + * original source text. See the Wikipedia entry on + * <a href="http://en.wikipedia.org/wiki/Rot-13">ROT13</a> + * for more information on this topic. + * + * @since 3.0 + * @author Michael Gentry + */ +public class Rot13PasswordEncoder implements PasswordEncoding +{ + /* (non-Javadoc) + * @see org.apache.cayenne.conf.PasswordEncoding#decodePassword(java.lang.String, java.lang.String) + */ + public String decodePassword(String encodedPassword, String salt) + { + return rotate(encodedPassword); + } + + /* (non-Javadoc) + * @see org.apache.cayenne.conf.PasswordEncoding#encodePassword(java.lang.String, java.lang.String) + */ + public String encodePassword(String normalPassword, String salt) + { + return rotate(normalPassword); + } + + /** + * Applies a ROT-13 Caesar cipher to the supplied value. Each letter in + * the supplied value is substituted with a new value rotated by 13 places + * in the alphabet. See <a href="http://en.wikipedia.org/wiki/ROT13">ROT13</a> + * for more information. + * <p> + * A Unix command to perform a ROT-13 cipher is: + * <pre>tr "[a-m][n-z][A-M][N-Z]" "[n-z][a-m][N-Z][A-M]"</pre> + * + * @param value The text to be rotated. + * @return The rotated text. + */ + public String rotate(String value) + { + int length = value.length(); + StringBuffer result = new StringBuffer(); + + for (int i = 0; i < length; i++) + { + char c = value.charAt(i); + + // If c is a letter, rotate it by 13. Numbers/symbols are untouched. + if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M')) + c += 13; + else if ((c >= 'n' && c <= 'z') || (c >= 'A' && c <= 'Z')) + c -= 13; + + result.append(c); + } + + return result.toString(); + } + + public static void main(String[] args) + { + Rot13PasswordEncoder encoder = new Rot13PasswordEncoder(); + + for (int i = 0; i < args.length; i++) + { + String string = args[i]; + + System.out.println(encoder.rotate(string)); + } + } +} Added: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/Rot47PasswordEncoder.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/Rot47PasswordEncoder.java?view=auto&rev=488331 ============================================================================== --- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/Rot47PasswordEncoder.java (added) +++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conf/Rot47PasswordEncoder.java Mon Dec 18 08:47:02 2006 @@ -0,0 +1,104 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + + +package org.apache.cayenne.conf; + +/** + * The ROT-47 password encoder passes the text of the database password + * through a simple Caesar cipher to obscure the password text. The ROT-47 + * cipher is similar to the ROT-13 cipher, but processes numbers and symbols + * as well. See the Wikipedia entry on + * <a href="http://en.wikipedia.org/wiki/Rot-13">ROT13</a> + * for more information on this topic. + * + * @since 3.0 + * @author Michael Gentry + */ +public class Rot47PasswordEncoder implements PasswordEncoding +{ + /* (non-Javadoc) + * @see org.apache.cayenne.conf.PasswordEncoding#decodePassword(java.lang.String, java.lang.String) + */ + public String decodePassword(String encodedPassword, String salt) + { + return rotate(encodedPassword); + } + + /* (non-Javadoc) + * @see org.apache.cayenne.conf.PasswordEncoding#encodePassword(java.lang.String, java.lang.String) + */ + public String encodePassword(String normalPassword, String salt) + { + return rotate(normalPassword); + } + + /** + * Applies a ROT-47 Caesar cipher to the supplied value. Each letter in + * the supplied value is substituted with a new value rotated by 47 places. + * See <a href="http://en.wikipedia.org/wiki/ROT13">ROT13</a> for more + * information (there is a subsection for ROT-47). + * <p> + * A Unix command to perform a ROT-47 cipher is: + * <pre>tr '!-~' 'P-~!-O'</pre> + * + * @param value The text to be rotated. + * @return The rotated text. + */ + public String rotate(String value) + { + int length = value.length(); + StringBuffer result = new StringBuffer(); + + for (int i = 0; i < length; i++) + { + char c = value.charAt(i); + + // Process letters, numbers, and symbols -- ignore spaces + if (c != ' ') + { + // Add 47 (it is ROT-47, after all). + c += 47; + + // If character is now above printable range, make it printable. + // Range of printable characters is ! (33) to ~ (126). A value + // of 127 (just above ~) would therefore get rotated down to a + // 33 (the !). + if (c > '~') + c -= 94; + } + + result.append(c); + } + + return result.toString(); + } + + public static void main(String[] args) + { + Rot47PasswordEncoder encoder = new Rot47PasswordEncoder(); + + for (int i = 0; i < args.length; i++) + { + String string = args[i]; + + System.out.println(encoder.rotate(string)); + } + } +} Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conn/DataSourceInfo.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conn/DataSourceInfo.java?view=diff&rev=488331&r1=488330&r2=488331 ============================================================================== --- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conn/DataSourceInfo.java (original) +++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/conn/DataSourceInfo.java Mon Dec 18 08:47:02 2006 @@ -22,6 +22,7 @@ import java.io.Serializable; +import org.apache.cayenne.conf.PasswordEncoding; import org.apache.cayenne.util.Util; /** @@ -41,6 +42,22 @@ protected int minConnections = 1; protected int maxConnections = 1; + // Constants for passwordLocation + public static final String PASSWORD_LOCATION_CLASSPATH = "classpath"; + public static final String PASSWORD_LOCATION_EXECUTABLE = "executable"; + public static final String PASSWORD_LOCATION_MODEL = "model"; + public static final String PASSWORD_LOCATION_URL = "url"; + + // Extended parameters + protected String passwordEncoderClass = PasswordEncoding.standardEncoders[0]; + protected String passwordEncoderSalt = ""; + protected String passwordSourceExecutable = ""; + protected String passwordSourceFilename = ""; + protected String passwordSourceModel = "Not Applicable"; + protected String passwordSourceUrl = ""; + protected String passwordLocation = PASSWORD_LOCATION_MODEL; + + public boolean equals(Object obj) { if (obj == this) return true; @@ -52,26 +69,33 @@ return false; DataSourceInfo dsi = (DataSourceInfo) obj; - if (!Util.nullSafeEquals(this.userName, dsi.userName)) - return false; + if (!Util.nullSafeEquals(this.userName, dsi.userName)) + return false; if (!Util.nullSafeEquals(this.password, dsi.password)) return false; - if (!Util.nullSafeEquals(this.jdbcDriver, dsi.jdbcDriver)) return false; - if (!Util.nullSafeEquals(this.dataSourceUrl, dsi.dataSourceUrl)) return false; - if (!Util.nullSafeEquals(this.adapterClassName, dsi.adapterClassName)) return false; - if (this.minConnections != dsi.minConnections) return false; - if (this.maxConnections != dsi.maxConnections) return false; + if (!Util.nullSafeEquals(this.passwordEncoderClass, dsi.passwordEncoderClass)) + return false; + if (!Util.nullSafeEquals(this.passwordEncoderSalt, dsi.passwordEncoderSalt)) + return false; + if (!Util.nullSafeEquals(this.passwordSourceFilename, dsi.passwordSourceFilename)) + return false; + if (!Util.nullSafeEquals(this.passwordSourceModel, dsi.passwordSourceModel)) + return false; + if (!Util.nullSafeEquals(this.passwordSourceUrl, dsi.passwordSourceUrl)) + return false; + if (!Util.nullSafeEquals(this.passwordLocation, dsi.passwordLocation)) + return false; return true; } @@ -99,7 +123,7 @@ else buf.append("**********"); - buf + buf .append("\n driver: ") .append(jdbcDriver) .append("\n db adapter class: ") @@ -110,6 +134,14 @@ .append(minConnections) .append("\n max. connections: ") .append(maxConnections) + .append("\n encoder class: ") + .append(passwordEncoderClass) + .append("\n encoder salt: ") + .append(passwordEncoderSalt) + .append("\n password location: ") + .append(passwordLocation) + .append("\n password source: ") + .append(getPasswordSource()) .append("\n]"); return buf.toString(); @@ -171,4 +203,167 @@ return dataSourceUrl; } + + public PasswordEncoding getPasswordEncoder() + { + PasswordEncoding encoder = null; + + try + { + encoder = (PasswordEncoding) Thread.currentThread().getContextClassLoader().loadClass(getPasswordEncoderClass()).newInstance(); + //encoder = (PasswordEncoding) Class.forName(getPasswordEncoderClass()).newInstance(); + } + catch (InstantiationException exception) + { + // TODO Auto-generated catch block + exception.printStackTrace(); + } + catch (IllegalAccessException exception) + { + // TODO Auto-generated catch block + exception.printStackTrace(); + } + catch (ClassNotFoundException exception) + { + // TODO Auto-generated catch block + exception.printStackTrace(); + } + + return encoder; + } + + /** + * @return the passwordEncoderClass + */ + public String getPasswordEncoderClass() + { + return passwordEncoderClass; + } + + /** + * @param passwordEncoderClass the passwordEncoderClass to set + */ + public void setPasswordEncoderClass(String passwordEncoderClass) + { + if (passwordEncoderClass == null) + this.passwordEncoderClass = PasswordEncoding.standardEncoders[0]; + else + this.passwordEncoderClass = passwordEncoderClass; + } + + /** + * @return the passwordEncoderSalt + */ + public String getPasswordEncoderSalt() + { + return passwordEncoderSalt; + } + + /** + * @param passwordEncoderSalt the passwordEncoderSalt to set + */ + public void setPasswordEncoderSalt(String passwordEncoderSalt) + { + this.passwordEncoderSalt = passwordEncoderSalt; + } + + /** + * @return the passwordLocationFilename + */ + public String getPasswordSourceFilename() + { + return passwordSourceFilename; + } + + /** + * @param passwordSourceFilename the passwordSourceFilename to set + */ + public void setPasswordSourceFilename(String passwordSourceFilename) + { + this.passwordSourceFilename = passwordSourceFilename; + } + + /** + * @return the passwordLocationModel + */ + public String getPasswordSourceModel() + { + return passwordSourceModel; + } + + /** + * @return the passwordLocationUrl + */ + public String getPasswordSourceUrl() + { + return passwordSourceUrl; + } + + /** + * @param passwordSourceUrl the passwordSourceUrl to set + */ + public void setPasswordSourceUrl(String passwordSourceUrl) + { + this.passwordSourceUrl = passwordSourceUrl; + } + + /** + * @return the passwordLocationExecutable + */ + public String getPasswordSourceExecutable() + { + return passwordSourceExecutable; + } + + /** + * @param passwordSourceExecutable the passwordSourceExecutable to set + */ + public void setPasswordSourceExecutable(String passwordSourceExecutable) + { + this.passwordSourceExecutable = passwordSourceExecutable; + } + + public String getPasswordSource() + { + if (getPasswordLocation().equals(PASSWORD_LOCATION_CLASSPATH)) + return getPasswordSourceFilename(); + else if (getPasswordLocation().equals(PASSWORD_LOCATION_EXECUTABLE)) + return getPasswordSourceExecutable(); + else if (getPasswordLocation().equals(PASSWORD_LOCATION_MODEL)) + return getPasswordSourceModel(); + else if (getPasswordLocation().equals(PASSWORD_LOCATION_URL)) + return getPasswordSourceUrl(); + + throw new RuntimeException("Invalid password source detected"); + } + + public void setPasswordSource(String passwordSource) + { + // The location for the model is omitted since it cannot change + if (getPasswordLocation().equals(PASSWORD_LOCATION_CLASSPATH)) + setPasswordSourceFilename(passwordSource); + else if (getPasswordLocation().equals(PASSWORD_LOCATION_EXECUTABLE)) + setPasswordSourceExecutable(passwordSource); + else if (getPasswordLocation().equals(PASSWORD_LOCATION_URL)) + setPasswordSourceUrl(passwordSource); + } + + /** + * @return the passwordLocation + */ + public String getPasswordLocation() + { + return passwordLocation; + } + + /** + * @param passwordLocation the passwordLocation to set + */ + public void setPasswordLocation(String passwordLocation) + { + if (passwordLocation == null) + this.passwordLocation = DataSourceInfo.PASSWORD_LOCATION_MODEL; + else + this.passwordLocation = passwordLocation; + } } Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/project/DataNodeFile.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/project/DataNodeFile.java?view=diff&rev=488331&r1=488330&r2=488331 ============================================================================== --- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/project/DataNodeFile.java (original) +++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/project/DataNodeFile.java Mon Dec 18 08:47:02 2006 @@ -62,7 +62,7 @@ public void save(PrintWriter out) throws Exception { ProjectDataSource src = (ProjectDataSource) nodeObj.getDataSource(); - new ConfigSaver().storeDataNode(out, src.getDataSourceInfo()); + new ConfigSaver().storeDataNode(out, getProject(), src.getDataSourceInfo()); } /** Modified: incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/JDBCDataSourceEditor.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/JDBCDataSourceEditor.java?view=diff&rev=488331&r1=488330&r2=488331 ============================================================================== --- incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/JDBCDataSourceEditor.java (original) +++ incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/JDBCDataSourceEditor.java Mon Dec 18 08:47:02 2006 @@ -21,6 +21,9 @@ import java.awt.Component; +import javax.swing.JOptionPane; + +import org.apache.cayenne.conn.DataSourceInfo; import org.apache.cayenne.modeler.CayenneModelerController; import org.apache.cayenne.modeler.ProjectController; import org.apache.cayenne.modeler.pref.DBConnectionInfo; @@ -48,30 +51,105 @@ protected void prepareBindings(BindingBuilder builder) { this.view = new JDBCDataSourceView(); - fieldAdapters = new ObjectBinding[6]; - fieldAdapters[0] = builder.bindToTextField( - view.getUserName(), - "node.dataSource.dataSourceInfo.userName"); - fieldAdapters[1] = builder.bindToTextField( - view.getPassword(), - "node.dataSource.dataSourceInfo.password"); - fieldAdapters[2] = builder.bindToTextField( - view.getUrl(), - "node.dataSource.dataSourceInfo.dataSourceUrl"); - fieldAdapters[3] = builder.bindToTextField( - view.getDriver(), - "node.dataSource.dataSourceInfo.jdbcDriver"); - fieldAdapters[4] = builder.bindToTextField( - view.getMaxConnections(), - "node.dataSource.dataSourceInfo.maxConnections"); - fieldAdapters[5] = builder.bindToTextField( - view.getMinConnections(), - "node.dataSource.dataSourceInfo.minConnections"); - + fieldAdapters = new ObjectBinding[10]; + fieldAdapters[0] = + builder.bindToTextField(view.getUserName(), "node.dataSource.dataSourceInfo.userName"); + fieldAdapters[1] = + builder.bindToTextField(view.getPassword(), "node.dataSource.dataSourceInfo.password"); + fieldAdapters[2] = + builder.bindToTextField(view.getUrl(), "node.dataSource.dataSourceInfo.dataSourceUrl"); + fieldAdapters[3] = + builder.bindToTextField(view.getDriver(), "node.dataSource.dataSourceInfo.jdbcDriver"); + fieldAdapters[4] = + builder.bindToTextField(view.getMaxConnections(), "node.dataSource.dataSourceInfo.maxConnections"); + fieldAdapters[5] = + builder.bindToTextField(view.getMinConnections(), "node.dataSource.dataSourceInfo.minConnections"); + fieldAdapters[6] = + builder.bindToComboSelection(view.getPasswordEncoder(), "node.dataSource.dataSourceInfo.passwordEncoderClass"); + fieldAdapters[7] = + builder.bindToTextField(view.getPasswordSalt(), "node.dataSource.dataSourceInfo.passwordEncoderSalt"); + fieldAdapters[8] = + builder.bindToComboSelection(view.getPasswordLocation(), "node.dataSource.dataSourceInfo.passwordLocation"); + fieldAdapters[9] = + builder.bindToTextField(view.getPasswordSource(), "node.dataSource.dataSourceInfo.passwordSource"); + // one way binding - builder.bindToAction(view.getSyncWithLocal(), "syncDataSourceAction()"); + builder.bindToAction(view.getPasswordEncoder(), "validatePasswordEncoderAction()"); + builder.bindToAction(view.getPasswordLocation(), "passwordLocationChangedAction()"); + builder.bindToAction(view.getSyncWithLocal(), "syncDataSourceAction()"); + } + + /** + * This action is called when a new password encoder is specified. It + * warns the user if the encoder class is not available and advises them + * to check their classpath. + */ + public void validatePasswordEncoderAction() + { + if (getNode() == null || getNode().getDataSource() == null) + return; + + DataSourceInfo dsi = ((ProjectDataSource) getNode().getDataSource()).getDataSourceInfo(); + + if (view.getPasswordEncoder().getSelectedItem().equals(dsi.getPasswordEncoderClass()) == false) + return; + + if (dsi.getPasswordEncoder() == null) + { + JOptionPane.showMessageDialog(getView(), + "A valid Password Encoder should be specified (check your CLASSPATH).", + "Invalid Password Encoder", + JOptionPane.ERROR_MESSAGE); + } + } + + /** + * Updates labels and editability of the password related fields. + * Called by the passwordLocationChangedAction method. + * + * @param isPasswordFieldEnabled True if password field is editable, false if not. + * @param isPasswordLocationEnabled True if password location field is editable, false if not. + * @param passwordText The password (which is obscured to the user, of course). + * @param passwordLocationLabel Label for the password location. + * @param passwordLocationText Text of the password location field. + */ + private void updatePasswordElements(boolean isPasswordFieldEnabled, + boolean isPasswordLocationEnabled, + String passwordText, + String passwordLocationLabel, + String passwordLocationText) + { + view.getPassword().setEnabled(isPasswordFieldEnabled); + view.getPassword().setText(passwordText); + view.getPasswordSource().setEnabled(isPasswordLocationEnabled); + view.getPasswordSourceLabel().setText(passwordLocationLabel); + view.getPasswordSource().setText(passwordLocationText); + } + /** + * This action is called whenever the password location is changed + * in the GUI pulldown. It changes labels and editability of the + * password fields depending on the option that was selected. + */ + public void passwordLocationChangedAction() + { + if (getNode() == null || getNode().getDataSource() == null) + return; + + DataSourceInfo dsi = ((ProjectDataSource) getNode().getDataSource()).getDataSourceInfo(); + + String selectedItem = (String) view.getPasswordLocation().getSelectedItem(); + + if (selectedItem.equals(DataSourceInfo.PASSWORD_LOCATION_CLASSPATH)) + updatePasswordElements(true, true, dsi.getPassword(), "Password Filename:", dsi.getPasswordSourceFilename()); + else if (selectedItem.equals(DataSourceInfo.PASSWORD_LOCATION_EXECUTABLE)) + updatePasswordElements(false, true, null, "Password Executable:", dsi.getPasswordSourceExecutable()); + else if (selectedItem.equals(DataSourceInfo.PASSWORD_LOCATION_MODEL)) + updatePasswordElements(true, false, dsi.getPassword(), "Password Source:", dsi.getPasswordSourceModel()); + else if (selectedItem.equals(DataSourceInfo.PASSWORD_LOCATION_URL)) + updatePasswordElements(false, true, null, "Password URL:", dsi.getPasswordSourceUrl()); } + public void syncDataSourceAction() { CayenneModelerController mainController = getApplication().getFrameController(); Modified: incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/JDBCDataSourceView.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/JDBCDataSourceView.java?view=diff&rev=488331&r1=488330&r2=488331 ============================================================================== --- incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/JDBCDataSourceView.java (original) +++ incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/JDBCDataSourceView.java Mon Dec 18 08:47:02 2006 @@ -20,12 +20,26 @@ package org.apache.cayenne.modeler.editor.datanode; import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Map; +import java.util.TreeMap; +import javax.swing.DefaultComboBoxModel; +import javax.swing.DefaultListCellRenderer; import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; +import org.apache.cayenne.conf.PasswordEncoding; +import org.apache.cayenne.conn.DataSourceInfo; +import org.apache.cayenne.modeler.util.CayenneWidgetFactory; + import com.jgoodies.forms.builder.PanelBuilder; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; @@ -35,30 +49,100 @@ */ public class JDBCDataSourceView extends JPanel { - protected JTextField driver; - protected JTextField url; - protected JTextField userName; + protected JTextField driver; + protected JTextField url; + protected JTextField userName; protected JPasswordField password; - protected JTextField minConnections; - protected JTextField maxConnections; - protected JButton syncWithLocal; + protected JComboBox passwordEncoder; + protected JComboBox passwordLocation; + protected JTextField passwordSalt; + protected JTextField passwordSource; + protected JLabel passwordSourceLabel; + protected JTextField minConnections; + protected JTextField maxConnections; + protected JButton syncWithLocal; + + private static final String PASSWORD_CLASSPATH = "Classpath Search (File System)"; + private static final String PASSWORD_EXECUTABLE = "Executable Program"; + private static final String PASSWORD_MODEL = "Cayenne Model"; + private static final String PASSWORD_URL = "URL (file:, http:, etc)"; + + private static final Object[] PASSWORD_LOCATIONS = new Object[] { + DataSourceInfo.PASSWORD_LOCATION_MODEL, + DataSourceInfo.PASSWORD_LOCATION_CLASSPATH, + DataSourceInfo.PASSWORD_LOCATION_EXECUTABLE, + DataSourceInfo.PASSWORD_LOCATION_URL }; + + private static final Map passwordSourceLabels = new TreeMap(); + + static + { + passwordSourceLabels.put(DataSourceInfo.PASSWORD_LOCATION_MODEL, PASSWORD_MODEL); + passwordSourceLabels.put(DataSourceInfo.PASSWORD_LOCATION_CLASSPATH, PASSWORD_CLASSPATH); + passwordSourceLabels.put(DataSourceInfo.PASSWORD_LOCATION_EXECUTABLE, PASSWORD_EXECUTABLE); + passwordSourceLabels.put(DataSourceInfo.PASSWORD_LOCATION_URL, PASSWORD_URL); + } + + + final class PasswordLocationRenderer extends DefaultListCellRenderer + { + public Component getListCellRendererComponent(JList list, + Object object, + int arg2, + boolean arg3, + boolean arg4) + { + if (object != null) + object = passwordSourceLabels.get(object); + else + object = PASSWORD_MODEL; + + return super.getListCellRendererComponent(list, object, arg2, arg3, arg4); + } + } public JDBCDataSourceView() { - driver = new JTextField(); - url = new JTextField(); - userName = new JTextField(); - password = new JPasswordField(); - minConnections = new JTextField(6); - maxConnections = new JTextField(6); - syncWithLocal = new JButton("Sync with Local"); + driver = new JTextField(); + url = new JTextField(); + userName = new JTextField(); + password = new JPasswordField(); + passwordEncoder = new JComboBox(); + passwordLocation = new JComboBox(); + passwordSource = new JTextField(); + passwordSalt = new JTextField(); + minConnections = new JTextField(6); + maxConnections = new JTextField(6); + syncWithLocal = new JButton("Sync with Local"); syncWithLocal.setToolTipText("Update from local DataSource"); + // init combo box choices + passwordEncoder.setModel(new DefaultComboBoxModel(PasswordEncoding.standardEncoders)); + passwordEncoder.setEditable(true); + + passwordLocation = CayenneWidgetFactory.createComboBox(); + passwordLocation.setRenderer(new PasswordLocationRenderer()); + // passwordSource.setModel(new DefaultComboBoxModel(passwordLocations)); +// EntityResolver resolver = mediator.getCurrentDataDomain().getEntityResolver(); + DefaultComboBoxModel passwordLocationModel = new DefaultComboBoxModel(PASSWORD_LOCATIONS); + //passwordSourceModel.setSelectedItem(query.getMetaData(resolver).getCachePolicy()); + passwordLocation.setModel(passwordLocationModel); +// passwordLocation.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent event) { +// Object source = passwordLocation.getModel().getSelectedItem(); +// System.out.println(source); +// } +// }); + + // assemble CellConstraints cc = new CellConstraints(); - FormLayout layout = new FormLayout( - "right:80dlu, 3dlu, fill:50dlu, 3dlu, fill:74dlu, 3dlu, fill:70dlu", - "p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p"); +// FormLayout layout = new FormLayout( +// "right:80dlu, 3dlu, fill:50dlu, 3dlu, fill:74dlu, 3dlu, fill:70dlu", +// "p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p"); + FormLayout layout = + new FormLayout("right:80dlu, 3dlu, fill:50dlu, 3dlu, fill:74dlu, 3dlu, fill:70dlu", // Columns + "p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p"); // Rows PanelBuilder builder = new PanelBuilder(layout); builder.setDefaultDialogBorder(); @@ -68,15 +152,32 @@ builder.add(driver, cc.xywh(3, 3, 5, 1)); builder.addLabel("DB URL:", cc.xy(1, 5)); builder.add(url, cc.xywh(3, 5, 5, 1)); - builder.addLabel("User Name:", cc.xy(1, 7)); + builder.addLabel("Username:", cc.xy(1, 7)); builder.add(userName, cc.xywh(3, 7, 5, 1)); builder.addLabel("Password:", cc.xy(1, 9)); builder.add(password, cc.xywh(3, 9, 5, 1)); - builder.addLabel("Min Connections:", cc.xy(1, 11)); - builder.add(minConnections, cc.xy(3, 11)); - builder.addLabel("Max Connections:", cc.xy(1, 13)); - builder.add(maxConnections, cc.xy(3, 13)); - builder.add(syncWithLocal, cc.xy(7, 13)); + + builder.addLabel("Password Encoder:", cc.xy(1, 11)); + builder.add(passwordEncoder, cc.xywh(3, 11, 5, 1)); + + builder.addLabel("Password Salt:", cc.xy(1, 13)); + builder.add(passwordSalt, cc.xywh(3, 13, 5, 1)); + + builder.addLabel("Cayenne supplied encoders do not require salting", cc.xywh(3, 15, 5, 1)); + + builder.addLabel("Password Location:", cc.xy(1, 17)); + builder.add(passwordLocation, cc.xywh(3, 17, 5, 1)); + + passwordSourceLabel = builder.addLabel("Password Source:", cc.xy(1, 19)); + builder.add(passwordSource, cc.xywh(3, 19, 5, 1)); + + builder.addLabel("Min Connections:", cc.xy(1, 21)); + builder.add(minConnections, cc.xy(3, 21)); + + builder.addLabel("Max Connections:", cc.xy(1, 23)); + builder.add(maxConnections, cc.xy(3, 23)); + + builder.add(syncWithLocal, cc.xy(7, 25)); this.setLayout(new BorderLayout()); this.add(builder.getPanel(), BorderLayout.CENTER); @@ -108,5 +209,45 @@ public JButton getSyncWithLocal() { return syncWithLocal; + } + + /** + * @return the passwordEncoder + */ + public JComboBox getPasswordEncoder() + { + return passwordEncoder; + } + + /** + * @return the passwordLocation + */ + public JComboBox getPasswordLocation() + { + return passwordLocation; + } + + /** + * @return the passwordSalt + */ + public JTextField getPasswordSalt() + { + return passwordSalt; + } + + /** + * @return the passwordSource + */ + public JTextField getPasswordSource() + { + return passwordSource; + } + + /** + * @return the passwordLocationLabel + */ + public JLabel getPasswordSourceLabel() + { + return passwordSourceLabel; } } Modified: incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/MainDataNodeEditor.java URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/MainDataNodeEditor.java?view=diff&rev=488331&r1=488330&r2=488331 ============================================================================== --- incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/MainDataNodeEditor.java (original) +++ incubator/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/datanode/MainDataNodeEditor.java Mon Dec 18 08:47:02 2006 @@ -58,10 +58,10 @@ protected static final String NO_LOCAL_DATA_SOURCE = "Select DataSource for Local Work..."; - final static String[] standardDataSourceFactories = new String[] { - DriverDataSourceFactory.class.getName(), - JNDIDataSourceFactory.class.getName(), DBCPDataSourceFactory.class.getName() - }; + final static String[] standardDataSourceFactories = + new String[] { DriverDataSourceFactory.class.getName(), + JNDIDataSourceFactory.class.getName(), + DBCPDataSourceFactory.class.getName() }; protected MainDataNodeView view;