001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.visualweb.dataconnectivity.project.datasource;
042:
043: import com.sun.rave.designtime.DesignBean;
044: import com.sun.rave.designtime.DesignProperty;
045: import java.beans.PropertyChangeEvent;
046: import java.beans.PropertyChangeListener;
047:
048: import org.netbeans.modules.visualweb.insync.Model;
049: import org.netbeans.modules.visualweb.insync.ModelSet;
050: import org.netbeans.modules.visualweb.insync.ModelSetListener;
051: import org.netbeans.modules.visualweb.insync.ModelSetsListener;
052: import org.netbeans.modules.visualweb.insync.live.LiveUnit;
053: import org.netbeans.modules.visualweb.insync.models.FacesModel;
054: import org.netbeans.modules.visualweb.insync.models.FacesModelSet;
055: import org.netbeans.modules.visualweb.project.jsf.services.RefreshService;
056: import org.netbeans.modules.visualweb.dataconnectivity.sql.DesignTimeDataSource;
057: import org.netbeans.modules.visualweb.dataconnectivity.sql.DesignTimeDataSourceHelper;
058: import org.netbeans.modules.visualweb.dataconnectivity.explorer.ProjectDataSourceNode;
059:
060: import org.netbeans.api.project.FileOwnerQuery;
061: import org.netbeans.api.project.Project;
062: import org.netbeans.api.project.ProjectManager;
063:
064: import org.netbeans.modules.visualweb.api.j2ee.common.RequestedJdbcResource;
065: import org.netbeans.modules.visualweb.insync.models.FacesModelSet;
066: import org.netbeans.spi.project.AuxiliaryConfiguration;
067:
068: import org.openide.ErrorManager;
069: import org.openide.filesystems.FileObject;
070:
071: import java.util.ArrayList;
072: import java.util.Arrays;
073: import java.util.Enumeration;
074: import java.util.HashMap;
075: import java.util.HashSet;
076: import java.util.Iterator;
077: import java.util.LinkedHashSet;
078: import java.util.List;
079: import java.util.Set;
080: import java.util.SortedSet;
081: import java.util.TreeSet;
082:
083: import javax.naming.NamingException;
084: import javax.xml.parsers.DocumentBuilder;
085: import javax.xml.parsers.DocumentBuilderFactory;
086: import org.netbeans.api.project.ui.OpenProjects;
087:
088: import org.netbeans.modules.visualweb.dataconnectivity.datasource.CurrentProject;
089: import org.netbeans.modules.visualweb.dataconnectivity.naming.DesignTimeInitialContextFactory;
090: import org.w3c.dom.Element;
091:
092: /**
093: * Manage, persists, and supply (on request) DataSource usage in multiple
094: * NB 4 projects.
095: *
096: * @author Joel Brown
097: */
098: public class ProjectDataSourceTracker {
099:
100: // static private final String DATASOURCE_NAMES = com.sun.rave.designtime.Constants.ContextData.DATASOURCE_NAMES;
101:
102: static private final String DATASOURCE_PREFIX = "java:comp/env/"; // NOI18N
103: static private final String NON_DEFAULT_DATASOURCE_PREFIX = "java:/"; // NOI18N
104:
105: static private final String HC_ELEMENT_NAME = "hardcoded-datasource-names"; // NOI18N
106: static private final String HC_ELEMENT_NAMESPACE = "http://creator.sun.com/project/datasources"; // NOI18N
107: static private final boolean HC_ELEMENT_SHARED = true;
108: static private final String HC_ATTRIBUTE_NAME = "value"; // NOI18N
109:
110: /*
111: * flag for controlling the initialization of an instance of DesignTimeInitialContext
112: */
113: static public boolean isInitialContextInitialized = false;
114:
115: /**
116: * list of DSTracker objects, one for each project
117: */
118: private HashMap<Project, DSTracker> trackers = new HashMap<Project, DSTracker>();
119:
120: protected ProjectTrackerContextListener insyncDataSourceListener = new ProjectTrackerContextListener();
121: protected OpenProjectsListener openProjectsListener = new OpenProjectsListener();
122:
123: /****
124: * This class is meant to be a singleton service
125: * instantiated via the dataconnectivity file's layer.xml.
126: */
127: private ProjectDataSourceTracker() {
128: // logInfo( this.toString() ) ;
129:
130: // TODO: add listener to insync here for Project openings
131: // and closings.
132: ModelSet.addModelSetsListener(insyncDataSourceListener);
133: OpenProjects.getDefault().addPropertyChangeListener(
134: openProjectsListener);
135: }
136:
137: private static ProjectDataSourceTracker this One = new ProjectDataSourceTracker();
138:
139: public static ProjectDataSourceTracker getInstance() {
140: return this One;
141: }
142:
143: public static RequestedJdbcResource[] getProjectDataSourceInfo(
144: Project project) throws NamingException {
145: return getDSTracker(project).getProjectDataSourceInfo();
146: }
147:
148: public static void addListener(Project project,
149: ProjectDataSourceListener listener) {
150: getDSTracker(project).addListener(listener);
151: }
152:
153: public static void removeListener(Project project,
154: ProjectDataSourceListener listener) {
155: getDSTracker(project).removeListener(listener);
156: }
157:
158: public static void addListener(Project project,
159: ProjectDataSourcesListener listener) {
160: getDSTracker(project).addListener(listener);
161: }
162:
163: public static void removeListener(Project project,
164: ProjectDataSourcesListener listener) {
165: getDSTracker(project).removeListener(listener);
166: }
167:
168: public static void removeHardcodedDataSource(Project project,
169: String dataSourceNames) {
170: getDSTracker(project).removeDataSource(dataSourceNames);
171: }
172:
173: public static void addHardcodedDataSource(Project project,
174: String dataSourceNames) {
175: getDSTracker(project).addHardcodedDataSource(dataSourceNames);
176: }
177:
178: public static String[] getDynamicDataSources(Project project) {
179: // Set the current project
180: CurrentProject.getInstance().setProject(project);
181:
182: return getDSTracker(project).getDynamicDataSources();
183: }
184:
185: public static String[] getHardcodedDataSources(Project project) {
186: return getDSTracker(project).getHardcodedDataSources();
187: }
188:
189: public static boolean isHardcodedDataSource(Project project,
190: String datasourceName) {
191: return getDSTracker(project).isHardcodedDataSource(
192: datasourceName);
193: }
194:
195: public static String[] getFileDataSourceList(Project project) {
196: return getDSTracker(project).getFileDataSourceList();
197: }
198:
199: // For saving the resolved data source, project import feature for Shortfin
200: public static void setResolvedDataSource(Project project,
201: String resolvedDataSource) {
202: getDSTracker(project).setResolvedDataSource(resolvedDataSource);
203: }
204:
205: public static String getResolvedDataSource(Project project) {
206: return getDSTracker(project).getResolvedDataSource();
207: }
208:
209: public static org.openide.nodes.Node getNode(Project project) {
210: return getDSTracker(project).getNode();
211: }
212:
213: public static Project[] getProjects() {
214: HashMap<Project, DSTracker> tt = getInstance().trackers;
215: return tt.keySet().toArray(new Project[tt.size()]);
216: }
217:
218: /***
219: * for a datasource, refresh designers that use that datasource.
220: * CURRENTLY: datasources is ignored, all projects are refreshed.
221: */
222: public static void refreshDesignerForDataSources(String datasources) {
223: // refreshes the designer panes for all the projects.
224: RefreshService designerRefresher = RefreshService.getDefault();
225: if (designerRefresher != null) {
226: Project[] projs = ProjectDataSourceTracker.getProjects();
227: for (int i = 0; i < projs.length; i++) {
228: // Ideally, we should check to see if this project uses the
229: // imported datasource - but assume not many are open so
230: // just refresh them ALL.
231: designerRefresher.refresh(projs[i]);
232: }
233: }
234: }
235:
236: public static synchronized DSTracker getDSTracker(Project project) {
237: DSTracker tracker = null;
238: tracker = getInstance().trackers.get(project);
239:
240: if (tracker == null) {
241: tracker = getInstance().addDSTracker(project);
242: }
243: return tracker;
244: }
245:
246: // Refresh data source nodes
247: public static void refreshDataSources(Project project) {
248: getDSTracker(project).fireChangeEvent();
249: }
250:
251: // Refresh data source references node
252: public static synchronized void refreshDataSourceReferences(
253: Project project) {
254: getDSTracker(project).fireProjectDSReferencesChangeEvent();
255: }
256:
257: /**
258: * Used for migrating Creator 2 projects, since a source file must be modeled, not the project
259: */
260: public static boolean isProjectModeled(final Project project) {
261: Iterator it = retrieveFileToModel(project).iterator();
262:
263: while (it.hasNext()) {
264: if (FacesModelSet.getFacesModelIfAvailable((FileObject) it
265: .next()) != null) {
266: return true;
267: }
268: }
269:
270: return false;
271: }
272:
273: private DSTracker addDSTracker(Project project) {
274: DSTracker newTracker = new DSTracker(project);
275: trackers.put(project, newTracker);
276:
277: return newTracker;
278: }
279:
280: /**
281: * Used to retrieve a source file from a Creator 2 project for modeling
282: */
283: private static List<FileObject> retrieveFileToModel(Project project) {
284: FileObject[] projContents = project.getProjectDirectory()
285: .getChildren();
286: List<FileObject> fileToModel = new ArrayList<FileObject>();
287: for (FileObject projItem : projContents) {
288: if (projItem.isFolder()) {
289: if (projItem.getName().equals("src")) {
290: Enumeration folders = projItem.getFolders(true);
291: // Locate the source file to model
292: while (folders.hasMoreElements()) {
293: FileObject projSrc = (FileObject) folders
294: .nextElement();
295: for (FileObject srcFile : projSrc.getChildren()) {
296: if (srcFile.existsExt("java")) {
297: fileToModel.add(srcFile);
298: break;
299: }
300: }
301: }
302: }
303: }
304: }
305:
306: return fileToModel;
307: }
308:
309: /***
310: * Listens to context changes from insync.
311: */
312: public class ProjectTrackerContextListener implements
313: ModelSetListener, ModelSetsListener {
314:
315: /*---------- ModelSetsListener------------*/
316: public void modelSetAdded(ModelSet modelSet) {
317: // add a listener to the modelSet
318: logInfo("adding listener for project "
319: + modelSet.getProject());
320: modelSet.addModelSetListener(insyncDataSourceListener);
321: }
322:
323: public void modelSetRemoved(ModelSet modelSet) {
324: // don't care for now.
325: modelSet.removeModelSetListener(insyncDataSourceListener);
326: }
327:
328: public void modelProjectChanged() {
329: // EAT Sorry Joel I added this and you may want to do something else ???
330: }
331:
332: /*---------- ModelSetListener------------*/
333: public void modelAdded(Model model) {
334: changeFileDataSources(model, false);
335: }
336:
337: public void modelChanged(Model model) {
338: changeFileDataSources(model, false);
339:
340: }
341:
342: public void modelRemoved(Model model) {
343: changeFileDataSources(model, true);
344: }
345:
346: /*---------- end of interface implements ------------*/
347:
348: public void changeFileDataSources(Model model,
349: boolean wasRemoved) {
350: logInfo("** received project DS name change event:"); // NOI18N
351:
352: // Create one instance of InitialContext if needed
353: if (!isInitialContextInitialized) { // NOI18N
354: DesignTimeInitialContextFactory
355: .setInitialContextFactoryBuilder();
356: }
357:
358: FacesModel fModel;
359: if (model instanceof FacesModel) {
360: fModel = (FacesModel) model;
361: if (fModel.isBusted())
362: return;
363: } else {
364: // others are possible, like ConfigModel.
365: // ignore these.
366: return;
367: }
368:
369: FileObject file = model.getFile();
370: String fname = file.getPath();
371: Project nb4Proj = FileOwnerQuery.getOwner(file);
372:
373: // trim the name down to the relative path (to the project dir)
374: String projRoot = nb4Proj.getProjectDirectory().getPath();
375: if (projRoot.length() < fname.length())
376: fname = fname.substring(projRoot.length());
377:
378: String newValue = null;
379: // get the new value if it was changed or added
380: if (!wasRemoved) {
381: LiveUnit daUnit = fModel.getLiveUnit();
382: if (daUnit != null) {
383: newValue = getDSNames(daUnit);
384: }
385: }
386:
387: if (isLoggable()) {
388: logInfo(" project: " + nb4Proj); // NOI18N
389: logInfo(" dir: " + projRoot); // NOI18N
390: logInfo(" file: " + fname); // NOI18N
391: logInfo(" DSnames:: " + newValue); // NOI18N
392: }
393: getDSTracker(nb4Proj).changeDataSourcesForFile(fname,
394: newValue);
395: }
396: }
397:
398: // look for dataSourceName property in the beans on this page.
399: private String getDSNames(LiveUnit daUnit) {
400: DesignBean[] beans = daUnit.getBeans();
401: StringBuffer names = new StringBuffer();
402: boolean doneFirst = false;
403: for (int i = 0; i < beans.length; i++) {
404: Class beanClass = beans[i].getBeanInfo()
405: .getBeanDescriptor().getBeanClass();
406: //TODO: "dataSourceName" within Rowset class will always be a valid datasource.
407: //TODO: However, a "dataSourceName" could be in *any* class, we just
408: // don't have a guarentee it represents a jdbc datasource from our server nav.
409: // HACK - check for RowSet of if system prop rave.cached is set.
410: if (javax.sql.RowSet.class.isAssignableFrom(beanClass)
411: || System.getProperty("rave.cached") != null) { // NOI18N
412: DesignProperty[] lps = beans[i].getProperties();
413: for (int j = 0; j < lps.length; j++) {
414: if (lps[j].getPropertyDescriptor().getName()
415: .equals("dataSourceName")) { //NOI18N
416: Object v = lps[j].getValue();
417: if (v instanceof String) {
418: if (doneFirst) {
419: names.append(",");
420: } else {
421: doneFirst = true;
422: }
423: names.append((String) v);
424: }
425: }
426: }
427: }
428: }
429: return names.toString();
430: }
431:
432: /*********
433: * this class tracks the data sources for a single project
434: */
435: private class DSTracker {
436: public DSTracker(Project proj) {
437: this .project = proj;
438: projectAux = (AuxiliaryConfiguration) project.getLookup()
439: .lookup(AuxiliaryConfiguration.class);
440: if (projectAux == null) {
441: logErrorInfo("no AuxiliaryConfiguration.class implementation for Project "
442: + project); // NOI18N
443: } else {
444: logInfo("we have an AuxiliaryConfiguration"); // NOI18N
445: }
446: restoreHardcodedDataSources();
447: }
448:
449: private ProjectDataSourceNode projectNode; // used in the project navigator
450:
451: private Project project = null;
452: private HashSet listeners = new HashSet();
453: private HashSet listenersForDSContainer = new HashSet();
454:
455: AuxiliaryConfiguration projectAux = null;
456:
457: /*** these array lists (fnames,fnameDataSources, and dynamicDataSourceSet)
458: * must be kept consistent.
459: * We do this my synchronizing access to all three via dynamicDataSourceSet.
460: */
461: // This is the list of filenames in this project
462: ArrayList fnames = new ArrayList(); // of String
463: // this is the list of datasources for each filename
464: ArrayList fnameDataSources = new ArrayList();
465: // this is the unique list of dynamic data sources in the project
466: TreeSet dynamicDataSouceSet = new TreeSet(); // of String.
467: // the "hardCoded" ones are added via the proj nav - not from insync/pages.
468: TreeSet hardCodedDataSourceSet = new TreeSet(); // of String
469: Element hcdsElement = null; // for storing in the project
470: String resolvedDataSource = null;
471:
472: public Project getProject() {
473: return project;
474: }
475:
476: /* the projectNode is displayed in the project Navigator
477: * We create a new one for every call. If the project is closed and
478: * then re-opened, we can't return the same node (can't reparent).
479: **/
480: public org.openide.nodes.Node getNode() {
481: projectNode = new ProjectDataSourceNode(project);
482: return projectNode;
483: }
484:
485: public String[] getDynamicDataSources() {
486: String[] retVal;
487: synchronized (dynamicDataSouceSet) {
488: retVal = (String[]) dynamicDataSouceSet
489: .toArray(new String[dynamicDataSouceSet.size()]);
490: }
491: return retVal;
492: }
493:
494: public boolean isHardcodedDataSource(String dataSourceName) {
495: return (hardCodedDataSourceSet.contains(dataSourceName));
496: }
497:
498: public String[] getHardcodedDataSources() {
499: return (String[]) hardCodedDataSourceSet
500: .toArray(new String[hardCodedDataSourceSet.size()]);
501: }
502:
503: public void addHardcodedDataSource(String dataSourceNames) {
504: String[] names = dataSourceNames.split(","); // NOI18N
505: for (int j = 0; j < names.length; j++) {
506: String newDs = names[j].trim();
507: if (newDs.length() > 0) {
508: boolean added = hardCodedDataSourceSet.add(newDs);
509: if (added) {
510: persistHardcodedDataSources();
511: }
512: }
513: }
514: }
515:
516: public void removeHardcodedDataSource(String dataSourceName) {
517: if (this .isHardcodedDataSource(dataSourceName)) {
518: hardCodedDataSourceSet.remove(dataSourceName);
519: logInfo("removed hard " + dataSourceName); // NOI18N
520: persistHardcodedDataSources();
521: }
522: }
523:
524: public void removeDataSource(String dataSourceName) {
525: hardCodedDataSourceSet.remove(dataSourceName);
526: logInfo("removed" + dataSourceName); // NOI18N
527: persistHardcodedDataSources();
528: }
529:
530: /***
531: * save the hard coded data source list and tell listeners
532: */
533: private void persistHardcodedDataSources() {
534:
535: if (projectAux == null) {
536: return;
537: }
538:
539: if (hcdsElement == null) {
540: restoreHardcodedDataSources();
541: if (hcdsElement == null) {
542: // things seriously screwed up, should never ever be here.
543: return;
544: }
545: }
546: String cvsString = this .composeCsv(hardCodedDataSourceSet);
547: logInfo("persisting hard data sources " + cvsString);
548: hcdsElement.setAttribute(HC_ATTRIBUTE_NAME, cvsString); // NOI18N
549:
550: projectAux.putConfigurationFragment(hcdsElement,
551: HC_ELEMENT_SHARED);
552:
553: //TODO: force a save of the project.
554: try {
555: ProjectManager.getDefault().saveProject(project);
556: } catch (java.io.IOException ioe) {
557: // should really never be here!
558: logErrorInfo("Could not save project!");
559: }
560:
561: // tell everybody.
562: fireChangeEvent();
563: }
564:
565: /***
566: * restore the list of hard coded listeners.
567: * If it's a new project, create the Element to store them.
568: * The element (either created or retrieved from the project)
569: * will be reused when saving.
570: */
571: private void restoreHardcodedDataSources() {
572:
573: if (projectAux == null)
574: return;
575:
576: // Ask the project if it has an exisiting Element
577: hcdsElement = projectAux.getConfigurationFragment(
578: HC_ELEMENT_NAME, HC_ELEMENT_NAMESPACE,
579: HC_ELEMENT_SHARED);
580: if (hcdsElement != null) {
581: String hcds = hcdsElement
582: .getAttribute(HC_ATTRIBUTE_NAME); // NOI18N
583: logInfo("Restoring hard DS for " + this .project);
584:
585: if (hcds != null) {
586: TreeSet newSet = new TreeSet();
587: String[] names = hcds.split(","); // NOI18N
588: for (int j = 0; j < names.length; j++) {
589: String newDs = names[j].trim();
590: if (newDs.length() > 0) {
591: boolean added = newSet.add(names[j]);
592: logInfo("restored hard DS " + names[j]);
593: }
594: }
595: this .hardCodedDataSourceSet = newSet;
596: // tell everybody.
597: fireChangeEvent();
598: }
599: } else {
600: // element not currently in the project, so create a new Element
601: try {
602: DocumentBuilderFactory docFactory = DocumentBuilderFactory
603: .newInstance();
604: DocumentBuilder db = docFactory
605: .newDocumentBuilder();
606: hcdsElement = db.newDocument().createElementNS(
607: HC_ELEMENT_NAMESPACE, HC_ELEMENT_NAME);
608: logInfo("new proj element for hard datasources created.");
609: } catch (javax.xml.parsers.ParserConfigurationException pce) {
610: // seriously screwed up.
611: hcdsElement = null;
612: logErrorInfo("could not create the document element for saving HC DS"); // NOI18N
613: }
614: }
615: }
616:
617: // For saving the resolved data source, project import feature for Shortfin
618: private void setResolvedDataSource(String resolvedDataSource) {
619: this .resolvedDataSource = resolvedDataSource;
620: }
621:
622: private String getResolvedDataSource() {
623: return resolvedDataSource;
624: }
625:
626: // notify listeners of change.
627: private void fireChangeEvent() {
628: if (!listeners.isEmpty()) {
629: String[] dynamicDataSources = getDynamicDataSources();
630: String[] hardcodedDataSources = getHardcodedDataSources();
631: for (Iterator i = listeners.iterator(); i.hasNext();) {
632: ProjectDataSourceListener pair = (ProjectDataSourceListener) i
633: .next();
634: pair
635: .dataSourceChange(new ProjectDataSourceChangeEvent(
636: dynamicDataSources,
637: hardcodedDataSources));
638: }
639: }
640: }
641:
642: private void fireProjectDSReferencesChangeEvent() {
643: if (!listenersForDSContainer.isEmpty()) {
644: String resolvedDataSource = getResolvedDataSource();
645: for (Iterator i = listenersForDSContainer.iterator(); i
646: .hasNext();) {
647: ProjectDataSourcesListener pair = (ProjectDataSourcesListener) i
648: .next();
649: pair
650: .dataSourcesChange(new ProjectDataSourcesChangeEvent(
651: resolvedDataSource));
652: }
653: }
654: }
655:
656: private String composeCsv(SortedSet set) {
657: String val = ""; // NOI18N
658: for (Iterator i = set.iterator(); i.hasNext();) {
659: if (val.length() != 0) {
660: val += ","; //NOI18N
661: }
662: val += (String) i.next();
663: }
664: return val;
665: }
666:
667: /***
668: * compose a list of jdbc info used in deployment.
669: */
670: public RequestedJdbcResource[] getProjectDataSourceInfo()
671: throws NamingException {
672:
673: // make sure insync has processed all files for dynamic datasources.
674: if (Boolean.getBoolean("vwp.designer.jsf.loadModelSync")) { // NOI18N
675: FacesModelSet.getInstance(project);
676: } else {
677: FacesModelSet.startModeling(project);
678: }
679:
680: TreeSet namesList = new TreeSet();
681: synchronized (dynamicDataSouceSet) {
682: /* get all DATASOURCE_NAMES properties associated with this project */
683: namesList.addAll(dynamicDataSouceSet);
684: }
685: namesList.addAll(hardCodedDataSourceSet);
686:
687: ArrayList retList = new ArrayList(); // unique list of data sources.
688: DesignTimeDataSourceHelper dsh = new DesignTimeDataSourceHelper();
689:
690: for (Iterator i = namesList.iterator(); i.hasNext();) {
691: String dsName = (String) i.next();
692: try {
693: DesignTimeDataSource ds = dsh
694: .getDataSourceFromFullName(dsName);
695:
696: // stripDATASOURCE_PREFIX is a hack for JBoss and other application servers due to differences in JNDI string format
697: // for issue 101812
698: retList.add(new RequestedJdbcResource(
699: stripDATASOURCE_PREFIX(dsName), ds
700: .getDriverClassName(), ds.getUrl(),
701: ds.getUsername(), ds.getPassword()));
702: } catch (NamingException e) {
703: // stripDATASOURCE_PREFIX is a hack for JBoss and other application servers due to differences in JNDI string format
704: // for issue 101812
705: retList.add(new RequestedJdbcResource(
706: stripDATASOURCE_PREFIX(dsName), null, null,
707: null, null));
708: }
709: }
710:
711: logInfo("returning RequestedJdbcResource array of length "
712: + retList.size()); // NOI18N
713: for (int j = 0; j < retList.size(); j++) {
714: RequestedJdbcResource rjr = (RequestedJdbcResource) retList
715: .get(j);
716: logInfo("resourceName : " + rjr.getResourceName()
717: + " "); // NOI18N
718: logInfo(" driverClassName: "
719: + rjr.getDriverClassName() + " "); // NOI18N
720: logInfo(" url : " + rjr.getUrl() + " "); // NOI18N
721: logInfo(" username : " + rjr.getUsername() + " "); // NOI18N
722: logInfo(" password : "
723: + DesignTimeDataSource.encryptPassword( // NOI18N
724: rjr.getPassword()) + " "); // NOI18N
725: }
726:
727: return (RequestedJdbcResource[]) retList
728: .toArray(new RequestedJdbcResource[retList.size()]);
729: }
730:
731: private String stripDATASOURCE_PREFIX(String orig) {
732: if (orig == null)
733: return orig;
734: if (orig.startsWith(NON_DEFAULT_DATASOURCE_PREFIX))
735: return orig.substring(NON_DEFAULT_DATASOURCE_PREFIX
736: .length());
737: if (!orig.startsWith(DATASOURCE_PREFIX))
738: return orig;
739: if (orig.equals(DATASOURCE_PREFIX))
740: return orig;
741: return orig.substring(DATASOURCE_PREFIX.length());
742: }
743:
744: private void addListener(ProjectDataSourceListener listener) {
745: listeners.add(listener);
746: }
747:
748: private void removeListener(ProjectDataSourceListener listener) {
749: listeners.remove(listener);
750: }
751:
752: private void addListener(ProjectDataSourcesListener listener) {
753: listenersForDSContainer.add(listener);
754: }
755:
756: private void removeListener(ProjectDataSourcesListener listener) {
757: listenersForDSContainer.remove(listener);
758: }
759:
760: public synchronized void changeDataSourcesForFile(String fname,
761: String newValue) {
762:
763: synchronized (dynamicDataSouceSet) {
764: int origSize = fnames.size();
765: boolean removeIt = false;
766: if (newValue == null || "".equals(newValue.trim())) { // NOI18N
767: removeIt = true;
768: }
769:
770: int positionInList = -1;
771: for (int icnt = 0; icnt < origSize; icnt++) {
772: if (fname.equals((String) fnames.get(icnt))) {
773: positionInList = icnt;
774: break;
775: }
776: }
777: if (positionInList >= 0) {
778: if (removeIt) {
779: if (isLoggable()) {
780: logInfo(" removing file from list:"); // NOI18N
781: logInfo(" file: " + fname); // NOI18N
782: logInfo(" DSnames:: " + newValue); // NOI18N
783: }
784: fnames.remove(positionInList);
785: fnameDataSources.remove(positionInList);
786: updateDesignDataSourceList();
787: } else {
788: String oldValue = (String) fnameDataSources
789: .get(positionInList);
790: if (!oldValue.equals(newValue)) {
791: // change the value
792: if (isLoggable()) {
793: logInfo(" changing file:"); // NOI18N
794: logInfo(" file: " + fname); // NOI18N
795: logInfo(" DSnames:: " + newValue); // NOI18N
796: }
797: fnameDataSources.set(positionInList,
798: newValue);
799: updateDesignDataSourceList();
800: } else {
801: if (isLoggable()) {
802: logInfo(" no change to file:"); // NOI18N
803: logInfo(" file: " + fname); // NOI18N
804: logInfo(" DSnames:: " + newValue); // NOI18N
805: }
806:
807: }
808: }
809: }
810: if (positionInList < 0 && !removeIt) {
811: // add it to the list.
812: fnames.add(fname);
813: fnameDataSources.add(newValue);
814: updateDesignDataSourceList();
815: }
816: }
817: }
818:
819: /****
820: * calculate the dynamic datasources in this project by merging the
821: * datasources used in each file.
822: */
823: private void updateDesignDataSourceList() {
824: if (isLoggable()) {
825: logInfo("-----possible update of list of dynamic data sources"); // NOI18N
826: }
827:
828: synchronized (dynamicDataSouceSet) {
829: TreeSet newSet = new TreeSet();
830: for (int icnt = 0; icnt < fnameDataSources.size(); icnt++) {
831: String wholeList = (String) fnameDataSources
832: .get(icnt);
833: if (wholeList != null) {
834: String[] names = wholeList.split(","); // NOI18N
835: for (int j = 0; j < names.length; j++) {
836: boolean added = newSet.add(names[j]);
837: }
838: }
839: }
840: /***
841: * since user could have just added a data source that was
842: * previously "hard coded", check for hard coded with the same name
843: * name remove if found.
844: **/
845: if (!newSet.isEmpty()) {
846: for (Iterator ii = newSet.iterator(); ii.hasNext();) {
847: String name = (String) ii.next();
848: if (isHardcodedDataSource(name)) {
849: removeDataSource(name);
850: logInfo(" removing hard " + name); // NOI18N
851: }
852: }
853: }
854:
855: if (isLoggable()) {
856: logInfo(" calculation of ds in project list:"); // NOI18N
857: logInfo(" old:: " + dynamicDataSouceSet); // NOI18N
858: logInfo(" new:: " + newSet); // NOI18N
859: }
860: // compare newSet to old Set
861: if (!newSet.equals(dynamicDataSouceSet)) {
862: // data source list has changed.
863: dynamicDataSouceSet = newSet;
864: fireChangeEvent();
865: }
866: }
867: }
868:
869: /**
870: * get the list of files with datasources for each.
871: * Used for debugging.
872: */
873: public String[] getFileDataSourceList() {
874: ArrayList retVal = new ArrayList();
875: StringBuffer sb;
876: synchronized (dynamicDataSouceSet) {
877: for (int icnt = 0; icnt < fnames.size(); icnt++) {
878: sb = new StringBuffer((String) fnames.get(icnt));
879: sb.append(" :: ").append(
880: (String) fnameDataSources.get(icnt)); // NOI18N
881: retVal.add(sb.toString());
882: }
883: for (Iterator i = hardCodedDataSourceSet.iterator(); i
884: .hasNext();) {
885: sb = new StringBuffer("Manually added :: "); // NOI18N
886: sb.append((String) i.next());
887: retVal.add(sb.toString());
888: }
889: }
890: return (String[]) retVal.toArray(new String[retVal.size()]);
891: }
892: }
893:
894: public class OpenProjectsListener implements PropertyChangeListener {
895:
896: public void propertyChange(PropertyChangeEvent event) {
897:
898: // The list of open projects has changed; clean up any old projects we may be holding on to.
899: if (OpenProjects.PROPERTY_OPEN_PROJECTS.equals(event
900: .getPropertyName())) {
901:
902: List<Project> oldOpenProjectsList = Arrays
903: .asList((Project[]) event.getOldValue());
904: List<Project> newOpenProjectsList = Arrays
905: .asList((Project[]) event.getNewValue());
906: Set<Project> closedProjectsSet = new LinkedHashSet<Project>(
907: oldOpenProjectsList);
908: closedProjectsSet.removeAll(newOpenProjectsList);
909: for (Project project : closedProjectsSet) {
910: // Project has been closed; remove it and the DSTracker from the map.
911: trackers.remove(project);
912: }
913: }
914: }
915: }
916:
917: private static final String LOGPREFIX = "PDST: "; // NOI18N
918: private static ErrorManager err = ErrorManager.getDefault()
919: .getInstance("rave.dbdatasource"); // NOI18N
920:
921: private static void logInfo(String msg) {
922: err.log(ErrorManager.INFORMATIONAL, LOGPREFIX + msg);
923: }
924:
925: private boolean isLoggable() {
926: return err.isLoggable(ErrorManager.INFORMATIONAL);
927: }
928:
929: private static void logErrorInfo(String msg) {
930: err.log(ErrorManager.ERROR, LOGPREFIX + msg);
931: }
932:
933: public String toString() {
934: StringBuffer sb = new StringBuffer(111);
935: sb.append("Data Source Tracker (" + trackers.size()
936: + " projects) \n"); // NOI18N
937: for (int icnt = 0; icnt < trackers.size(); icnt++) {
938: DSTracker dst = (DSTracker) trackers.get(icnt);
939: sb.append("*" + icnt + "*Project "); // NOI18N
940: sb.append(dst.project.getProjectDirectory()).append(
941: " [Dynamic: "); // NOI18N
942: sb.append(dst.getDynamicDataSources()).append(" | Hard: "); // NOI18N
943: sb.append(dst.getHardcodedDataSources()).append(" ] \n"); // NOI18N
944: }
945: return sb.toString();
946: }
947:
948: }
|