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-2006 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:
042: package org.netbeans.modules.j2ee.dd.api.client;
043:
044: import java.io.File;
045: import java.io.FileInputStream;
046: import java.io.IOException;
047: import java.lang.ref.WeakReference;
048: import java.math.BigDecimal;
049: import java.net.URL;
050: import java.util.HashMap;
051: import java.util.Map;
052: import org.netbeans.modules.j2ee.dd.api.common.CommonDDBean;
053: import org.netbeans.modules.j2ee.dd.impl.client.ClientParseUtils;
054: import org.netbeans.modules.j2ee.dd.impl.client.AppClientProxy;
055: import org.netbeans.modules.j2ee.dd.impl.common.DDUtils;
056: import org.netbeans.modules.schema2beans.BaseBean;
057: import org.openide.filesystems.FileChangeAdapter;
058: import org.openide.filesystems.FileEvent;
059: import org.openide.filesystems.FileObject;
060: import org.openide.util.Exceptions;
061: import org.xml.sax.SAXException;
062: import org.xml.sax.SAXParseException;
063:
064: /**
065: * Provides access to Deployment Descriptor root
066: * ({@link org.netbeans.modules.j2ee.dd.api.client.AppClient} object).
067: *
068: * @author Milan Kuchtiak
069: */
070: public final class DDProvider {
071:
072: private static final DDProvider ddProvider = new DDProvider();
073: private final FCA fileChangeListener;
074: private final Map<URL, WeakReference<AppClientProxy>> ddMap;
075: private final Map<URL, WeakReference<AppClient>> baseBeanMap;
076: private final Map<URL, SAXParseException> errorMap;
077:
078: private DDProvider() {
079: ddMap = new HashMap<URL, WeakReference<AppClientProxy>>(5);
080: baseBeanMap = new HashMap<URL, WeakReference<AppClient>>(5);
081: errorMap = new HashMap<URL, SAXParseException>(5);
082: fileChangeListener = new FCA();
083: }
084:
085: /**
086: * Accessor method for DDProvider singleton
087: * @return DDProvider object
088: */
089: public static DDProvider getDefault() {
090: return ddProvider;
091: }
092:
093: /**
094: * Returns the root of deployment descriptor bean graph for java.io.File object.
095: *
096: * @param f File representing the web.xml file
097: * @return appClient object - root of the deployment descriptor bean graph
098: */
099: public AppClient getDDRoot(File f) throws IOException, SAXException {
100: return DDUtils.createAppClient(new FileInputStream(f),
101: ClientParseUtils.getVersion(new FileInputStream(f)));
102: }
103:
104: /**
105: * Returns the root of deployment descriptor bean graph for given file object.
106: * The method is useful for clints planning to read only the deployment descriptor
107: * or to listen to the changes.
108: * @param fo FileObject representing the application-client.xml file
109: * @return AppClient object - root of the deployment descriptor bean graph
110: */
111: public synchronized AppClient getDDRoot(FileObject fo)
112: throws IOException {
113: if (fo == null) {
114: return null;
115: }
116: AppClientProxy appClient = null;
117:
118: synchronized (ddMap) {
119: appClient = getFromCache(fo);
120: if (appClient != null) {
121: return appClient;
122: }
123: }
124:
125: fo.addFileChangeListener(fileChangeListener);
126:
127: String version = null;
128: SAXParseException error = null;
129: try {
130: AppClient original = null;
131: synchronized (baseBeanMap) {
132: original = getOriginalFromCache(fo);
133: if (original == null) {
134: version = ClientParseUtils.getVersion(fo
135: .getInputStream());
136: // preparsing
137: error = ClientParseUtils.parse(fo);
138: original = DDUtils.createAppClient(fo
139: .getInputStream(), version);
140: baseBeanMap.put(fo.getURL(),
141: new WeakReference<AppClient>(original));
142: errorMap.put(fo.getURL(), error);
143: } else {
144: BigDecimal orgVersion = original.getVersion();
145: if (orgVersion != null) {
146: version = orgVersion.toPlainString();
147: }
148: error = errorMap.get(fo.getURL());
149: }
150: }
151: appClient = new AppClientProxy(original, version);
152: if (error != null) {
153: appClient.setStatus(AppClient.STATE_INVALID_PARSABLE);
154: appClient.setError(error);
155: }
156: } catch (SAXException ex) {
157: appClient = new AppClientProxy(null, version);
158: appClient.setStatus(AppClient.STATE_INVALID_UNPARSABLE);
159: if (ex instanceof SAXParseException) {
160: appClient.setError((SAXParseException) ex);
161: } else if (ex.getException() instanceof SAXParseException) {
162: appClient.setError((SAXParseException) ex
163: .getException());
164: }
165: }
166: synchronized (ddMap) {
167: AppClient cached = getFromCache(fo);
168: if (cached != null) {
169: return cached;
170: }
171: ddMap.put(fo.getURL(), new WeakReference<AppClientProxy>(
172: appClient));
173: }
174: return appClient;
175: }
176:
177: /**
178: * Returns the root of deployment descriptor bean graph for given file object.
179: * The method is useful for clients planning to modify the deployment descriptor.
180: * Finally the {@link org.netbeans.modules.j2ee.dd.api.ejb.EjbJar#write(org.openide.filesystems.FileObject)} should be used
181: * for writing the changes.
182: * @param fo FileObject representing the ejb-jar.xml file
183: * @return EjbJar object - root of the deployment descriptor bean graph
184: */
185: public AppClient getDDRootCopy(FileObject fo) throws IOException {
186: return (AppClient) getDDRoot(fo).clone();
187: }
188:
189: private AppClientProxy getFromCache(FileObject fo)
190: throws IOException {
191: if (fo == null) {
192: return null;
193: }
194: WeakReference<AppClientProxy> wr = ddMap.get(fo.getURL());
195: if (wr == null) {
196: return null;
197: }
198: AppClientProxy appClient = wr.get();
199: if (appClient == null) {
200: ddMap.remove(fo.getURL());
201: }
202: return appClient;
203: }
204:
205: private AppClient getOriginalFromCache(FileObject fo)
206: throws IOException {
207: WeakReference<AppClient> wr = baseBeanMap.get(fo.getURL());
208: if (wr == null) {
209: return null;
210: }
211: AppClient appClient = wr.get();
212: if (appClient == null) {
213: baseBeanMap.remove(fo.getURL());
214: errorMap.remove(fo.getURL());
215: }
216: return appClient;
217: }
218:
219: /** Convenient method for getting the BaseBean object from CommonDDBean object.
220: * The j2eeserver module needs BaseBean to implement jsr88 API.
221: * This is a temporary workaround until the implementation of jsr88 moves into ddapi
222: * or the implementation in j2eeserver gets changed.
223: * @deprecated do not use - temporary workaround that exposes the schema2beans implementation
224: */
225: public BaseBean getBaseBean(CommonDDBean bean) {
226: BaseBean result;
227: if (bean instanceof BaseBean) {
228: result = (BaseBean) bean;
229: } else if (bean instanceof AppClientProxy) {
230: result = (BaseBean) ((AppClientProxy) bean).getOriginal();
231: } else {
232: result = null;
233: }
234: return result;
235: }
236:
237: private class FCA extends FileChangeAdapter {
238:
239: public void fileChanged(FileEvent evt) {
240: FileObject fo = evt.getFile();
241: try {
242: synchronized (ddMap) {
243: synchronized (baseBeanMap) {
244: AppClientProxy appClient = getFromCache(fo);
245: AppClient orig = getOriginalFromCache(fo);
246: if (appClient != null) {
247: String version = null;
248: try {
249: version = ClientParseUtils
250: .getVersion(fo.getInputStream());
251: // preparsing
252: SAXParseException error = ClientParseUtils
253: .parse(fo);
254: if (error != null) {
255: appClient.setError(error);
256: appClient
257: .setStatus(AppClient.STATE_INVALID_PARSABLE);
258: } else {
259: appClient.setError(null);
260: appClient
261: .setStatus(AppClient.STATE_VALID);
262: }
263: AppClient original = DDUtils
264: .createAppClient(fo
265: .getInputStream(),
266: version);
267: baseBeanMap.put(fo.getURL(),
268: new WeakReference<AppClient>(
269: original));
270: errorMap.put(fo.getURL(), appClient
271: .getError());
272: appClient.merge(original,
273: AppClient.MERGE_UPDATE);
274: } catch (SAXException ex) {
275: if (ex instanceof SAXParseException) {
276: appClient
277: .setError((SAXParseException) ex);
278: } else if (ex.getException() instanceof SAXParseException) {
279: appClient
280: .setError((SAXParseException) ex
281: .getException());
282: }
283: appClient
284: .setStatus(AppClient.STATE_INVALID_UNPARSABLE);
285: appClient.setOriginal(null);
286: appClient.setProxyVersion(version);
287: }
288: } else if (orig != null) {
289: String version = null;
290: try {
291: version = ClientParseUtils
292: .getVersion(fo.getInputStream());
293: AppClient original = DDUtils
294: .createAppClient(fo
295: .getInputStream(),
296: version);
297: if (original.getClass().equals(
298: orig.getClass())) {
299: orig.merge(original,
300: AppClient.MERGE_UPDATE);
301: } else {
302: baseBeanMap
303: .put(
304: fo.getURL(),
305: new WeakReference<AppClient>(
306: original));
307: }
308: } catch (SAXException ex) {
309: baseBeanMap.remove(fo.getURL());
310: }
311: }
312: }
313: }
314: } catch (IOException ex) {
315: Exceptions.printStackTrace(ex);
316: }
317: }
318:
319: }
320:
321: }
|