001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.aop.deployment;
023:
024: import java.io.File;
025: import java.net.URL;
026: import java.util.Iterator;
027:
028: import javax.management.InstanceNotFoundException;
029: import javax.management.MBeanInfo;
030: import javax.management.MBeanServer;
031: import javax.management.MalformedObjectNameException;
032: import javax.management.Notification;
033: import javax.management.ObjectName;
034:
035: import org.jboss.aop.AspectAnnotationLoader;
036: import org.jboss.aop.AspectManager;
037: import org.jboss.aop.AspectXmlLoader;
038: import org.jboss.deployment.DeploymentException;
039: import org.jboss.deployment.DeploymentInfo;
040: import org.jboss.deployment.DeploymentState;
041: import org.jboss.deployment.SubDeployer;
042: import org.jboss.deployment.SubDeployerSupport;
043: import org.jboss.mx.loading.HeirarchicalLoaderRepository3;
044: import org.jboss.mx.loading.LoaderRepository;
045: import org.jboss.util.file.ArchiveBrowser;
046: import org.jboss.util.file.ClassFileFilter;
047: import org.w3c.dom.Document;
048: import org.w3c.dom.Element;
049: import org.w3c.dom.Node;
050: import org.w3c.dom.NodeList;
051:
052: /**
053: * Deployer for Aspects
054: *
055: * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
056: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
057: * @version $Revision: 60560 $
058: * @jmx:mbean name="jboss.aspect:AspectDeployer"
059: * extends="org.jboss.deployment.SubDeployerMBean"
060: */
061: public class AspectDeployer extends SubDeployerSupport implements
062: SubDeployer, AspectDeployerMBean {
063:
064: /**
065: * Default CTOR used to set default values to the Suffixes and RelativeOrder
066: * attributes. Those are read at subdeployer registration time by the MainDeployer
067: * to alter its SuffixOrder.
068: */
069: public AspectDeployer() {
070: initializeMainDeployer();
071: }
072:
073: /**
074: * Set the suffixes and relative order attributes.
075: *
076: * Those are read at subdeployer registration time by the MainDeployer
077: * to update its SuffixOrder list.
078: */
079: protected void initializeMainDeployer() {
080: setSuffixes(new String[] { ".aop", "-aop.xml" });
081: setRelativeOrder(100);
082: }
083:
084: /**
085: * Returns true if this deployer can deploy the given DeploymentInfo.
086: *
087: * @return True if this deployer can deploy the given DeploymentInfo.
088: * @jmx:managed-operation
089: */
090: public boolean accepts(DeploymentInfo di) {
091: String urlStr = di.url.toString();
092: return urlStr.endsWith(".aop") || urlStr.endsWith(".aop/")
093: || urlStr.endsWith("-aop.xml");
094: }
095:
096: /**
097: * Describe <code>init</code> method here.
098: *
099: * @param di a <code>DeploymentInfo</code> value
100: * @throws DeploymentException if an error occurs
101: * @jmx:managed-operation
102: */
103: public void init(DeploymentInfo di) throws DeploymentException {
104: try {
105: if (di.watch == null) {
106: // resolve the watch
107: if (di.url.getProtocol().equals("file")) {
108: File file = new File(di.url.getFile());
109:
110: // If not directory we watch the package
111: if (!file.isDirectory()) {
112: di.watch = di.url;
113: }
114: // If directory we watch the xml files
115: else {
116: di.watch = new URL(di.url,
117: "META-INF/jboss-aop.xml");
118: }
119: } else {
120: // We watch the top only, no directory support
121: di.watch = di.url;
122: }
123: }
124: } catch (Exception e) {
125: log.error("failed to parse AOP document: ", e);
126: throw new DeploymentException(e);
127: }
128: super .init(di);
129: }
130:
131: /**
132: * Describe <code>create</code> method here.
133: *
134: * @param di a <code>DeploymentInfo</code> value
135: * @throws DeploymentException if an error occurs
136: * @jmx:managed-operation
137: */
138: public void create(DeploymentInfo di) throws DeploymentException {
139: ClassLoader old = Thread.currentThread()
140: .getContextClassLoader();
141: try {
142: URL docURL = getDocUrl(di);
143: ClassLoader scl = getScopedClassLoader(di, docURL);
144:
145: if (scl != null) {
146: log.info("AOP deployment is scoped using classloader "
147: + scl);
148: }
149:
150: Thread.currentThread().setContextClassLoader(di.ucl);
151: if (!di.isXML) {
152: Iterator it = ArchiveBrowser.getBrowser(di.localUrl,
153: new ClassFileFilter());
154: AspectManager manager = (scl != null) ? AspectManager
155: .instance(scl) : AspectManager.instance();
156: AspectAnnotationLoader loader = new AspectAnnotationLoader(
157: manager);
158: loader.setClassLoader(scl);
159: loader.deployInputStreamIterator(it);
160: }
161:
162: AspectXmlLoader.deployXML(docURL, scl);
163: Notification msg = new Notification("AOP Deploy", this ,
164: getNextNotificationSequenceNumber());
165: sendNotification(msg);
166: log.debug("Deployed AOP: " + di.url);
167: } catch (Exception ex) {
168: ex.printStackTrace();
169: throw new DeploymentException(ex);
170: } finally {
171: Thread.currentThread().setContextClassLoader(old);
172: }
173: }
174:
175: /**
176: * The <code>start</code> method starts all the mbeans in this DeploymentInfo..
177: *
178: * @param di a <code>DeploymentInfo</code> value
179: * @throws DeploymentException if an error occurs
180: * @jmx:managed-operation
181: */
182: public void start(DeploymentInfo di) throws DeploymentException {
183: }
184:
185: /**
186: * Undeploys the package at the url string specified. This will: Undeploy
187: * packages depending on this one. Stop, destroy, and unregister all the
188: * specified mbeans Unload this package and packages this package deployed
189: * via the classpath tag. Keep track of packages depending on this one that
190: * we undeployed so that they can be redeployed should this one be
191: * redeployed.
192: *
193: * @param di the <code>DeploymentInfo</code> value to stop.
194: * @jmx:managed-operation
195: */
196: public void stop(DeploymentInfo di)
197: //throws DeploymentException
198: {
199: // Can happen on shutdown with a nested .aop module
200: // which is first stopped then undeployed.
201: if (di.state != DeploymentState.STARTED) {
202: log.debug("Ignoring request to stop '" + di.url
203: + "', current state: " + di.state);
204: return;
205: }
206:
207: log.debug("undeploying document " + di.url);
208: ClassLoader old = Thread.currentThread()
209: .getContextClassLoader();
210: try {
211: Thread.currentThread().setContextClassLoader(di.ucl);
212: if (!di.isXML) {
213: Iterator it = ArchiveBrowser.getBrowser(di.localUrl,
214: new ClassFileFilter());
215: AspectAnnotationLoader loader = new AspectAnnotationLoader(
216: AspectManager.instance());
217: loader.undeployInputStreamIterator(it);
218:
219: }
220:
221: URL docURL = getDocUrl(di);
222: //long start = System.currentTimeMillis();
223: AspectXmlLoader.undeployXML(docURL);
224: AspectManager.instance().unregisterClassLoader(di.ucl);
225:
226: /*
227: System.out.println("************************");
228: System.out.println("undeploy took: " + (System.currentTimeMillis() - start));
229: System.out.println("************************");
230: */
231: Notification msg = new Notification("AOP Undeploy", this ,
232: getNextNotificationSequenceNumber());
233: sendNotification(msg);
234: } catch (Exception ex) {
235: log.error("failed to stop", ex);
236: } finally {
237: Thread.currentThread().setContextClassLoader(old);
238: }
239: }
240:
241: /**
242: * Describe <code>destroy</code> method here.
243: *
244: * @param di a <code>DeploymentInfo</code> value
245: * @jmx:managed-operation
246: */
247: public void destroy(DeploymentInfo di)
248: //throws DeploymentException
249: {
250: }
251:
252: /**
253: * The startService method gets the mbeanProxies for MainDeployer
254: * and ServiceController, used elsewhere.
255: *
256: * @throws Exception if an error occurs
257: */
258: protected void startService() throws Exception {
259: super .startService();
260: }
261:
262: protected ObjectName getObjectName(MBeanServer server,
263: ObjectName name) throws MalformedObjectNameException {
264: return name == null ? OBJECT_NAME : name;
265: }
266:
267: private URL getDocUrl(DeploymentInfo di) throws DeploymentException {
268: URL docURL = di.localUrl;
269: if (di.isXML == false)
270: docURL = di.localCl.findResource("META-INF/jboss-aop.xml");
271: // Validate that the descriptor was found
272: if (docURL == null)
273: throw new DeploymentException(
274: "Failed to find META-INF/jboss-aop.xml");
275: return docURL;
276: }
277:
278: private ClassLoader getScopedClassLoader(DeploymentInfo di,
279: URL docUrl) throws Exception {
280: //Scoped AOP deployments are only available when deployed as part of a scoped sar, ear etc.
281: //It can contain an aop.xml file, or it can be part of a .aop file
282: //Linking a standalone -aop.xml file onto a scoped deployment is not possible at the moment
283: if (JBossScopedClassLoaderHelper.isScopedClassLoader(di.ucl)) {
284: return di.ucl;
285: }
286:
287: LoaderRepository attachToRepository = getLoaderRepositoryIfAttaching(
288: di, docUrl);
289: if (attachToRepository != null) {
290: di.ucl.setRepository(attachToRepository);
291: attachToRepository.addClassLoader(di.ucl);
292: return di.ucl;
293: }
294:
295: return null;
296: }
297:
298: private LoaderRepository getLoaderRepositoryIfAttaching(
299: DeploymentInfo di, URL docUrl) throws Exception {
300: if (di.parent == null) {
301: //We are a top-level deployment, check if we are meant to be attaching to a scoped repository
302:
303: //TODO Use AspectXmlLoader.loadURL once AOP is added to repository
304: Document doc = AspectXmlLoader.loadURL(docUrl);
305: Element top = doc.getDocumentElement();
306: NodeList children = top.getChildNodes();
307: int childlength = children.getLength();
308: for (int i = 0; i < childlength; i++) {
309: Node child = children.item(i);
310: if (child.getNodeType() == Node.ELEMENT_NODE) {
311: Element element = (Element) child;
312: if (element.getTagName()
313: .equals("loader-repository")) {
314: String repositoryName = null;
315: NodeList nestedChildren = child.getChildNodes();
316: int nestedChildLength = nestedChildren
317: .getLength();
318: for (int j = 0; j < nestedChildLength; j++) {
319: Node nestedChild = nestedChildren.item(j);
320: if (nestedChild.getNodeType() == Node.TEXT_NODE
321: || nestedChild.getNodeType() == Node.CDATA_SECTION_NODE) {
322: repositoryName = nestedChild
323: .getNodeValue().trim();
324: break;
325: }
326: }
327: log
328: .info("Should attach deployment to loader repository "
329: + repositoryName);
330: ObjectName on;
331: try {
332: on = new ObjectName(repositoryName);
333: } catch (MalformedObjectNameException e) {
334: throw new RuntimeException(
335: "loader-repository='"
336: + repositoryName
337: + "' is not a valid object name",
338: e);
339: }
340:
341: try {
342: MBeanInfo info = server.getMBeanInfo(on);
343: } catch (InstanceNotFoundException e) {
344: log
345: .warn("No scoped loader repository exists with the name "
346: + on);
347: }
348:
349: LoaderRepository repository = (LoaderRepository) server
350: .getAttribute(on, "Instance");
351: if (repository instanceof HeirarchicalLoaderRepository3) {
352: return repository;
353: } else {
354: log
355: .warn("Loader repository "
356: + on
357: + " is not a isolated loader repository. Deploying aspects in default domain.");
358: }
359: }
360: }
361: }
362: }
363:
364: return null;
365: }
366: }
|