blob: bcd32c1779b0c5324666050ee10642dcfc8ffc63 [file] [log] [blame]
/*
* 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.felix.ipojo.manipulation;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.felix.ipojo.metadata.Attribute;
import org.apache.felix.ipojo.metadata.Element;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
/**
* iPOJO Bytecode Manipulator.
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*
*/
public class Manipulator {
/**
* Store the visited fields : [name fo the field, type of the field].
*/
private Map m_fields = new HashMap();
/**
* Store the interface implemented by the class.
*/
private String[] m_interfaces = new String[0];
/**
* Store the methods list.
*/
private List m_methods = new ArrayList();
/**
* Manipulate the class.
*
* @param name : The name of the class
* @param outputDirectory : output directory where the class if stored.
* @return true if the class is correctly manipulated.
* @throws Exception : throwed if the manipulation failed.
*/
public boolean manipulate(String name, File outputDirectory) throws Exception {
// Init field, itfs and methods
m_fields = new HashMap();
m_interfaces = new String[0];
m_methods = new ArrayList();
// gets an input stream to read the bytecode of the class
String path = outputDirectory + "/" + name.replace('.', '/') + ".class";
File clazz = new File(path);
if (!clazz.exists()) {
return false;
}
URL url = clazz.toURL();
// if (url == null) { throw new ClassNotFoundException(name); }
ManipulationProperty.getLogger().log(ManipulationProperty.INFO, "Manipulate the class file : " + clazz.getAbsolutePath());
InputStream is1 = url.openStream();
// First check if the class is already manipulated :
ClassReader ckReader = new ClassReader(is1);
ClassChecker ck = new ClassChecker();
ckReader.accept(ck, ClassReader.SKIP_FRAMES);
is1.close();
m_fields = ck.getFields();
// Get interface and remove POJO interface is presents
String[] its = ck.getInterfaces();
List l = new ArrayList();
for (int i = 0; i < its.length; i++) {
l.add(its[i]);
}
l.remove("org/apache/felix/ipojo/Pojo");
m_interfaces = new String[l.size()];
for (int i = 0; i < m_interfaces.length; i++) {
m_interfaces[i] = ((String) l.get(i)).replace('/', '.');
}
// Get the method list
// Remove iPOJO methods
for (int i = 0; i < ck.getMethods().size(); i++) {
MethodDescriptor method = (MethodDescriptor) ck.getMethods().get(i);
if (!(method.getName().startsWith("_get") || // Avoid getter method
method.getName().startsWith("_set") || // Avoid setter method
method.getName().equals("_setComponentManager") || // Avoid the set method
method.getName().equals("getComponentInstance"))) { // Avoid the getComponentInstance method
System.err.println(" Add the method : " + method);
m_methods.add(method);
}
}
if (!ck.isalreadyManipulated()) {
// Manipulation ->
// Add the _setComponentManager method
// Instrument all fields
InputStream is2 = url.openStream();
ClassReader cr0 = new ClassReader(is2);
ClassWriter cw0 = new ClassWriter(ClassWriter.COMPUTE_MAXS);
PojoAdapter preprocess = new PojoAdapter(cw0);
cr0.accept(preprocess, ClassReader.SKIP_FRAMES);
is2.close();
try {
FileOutputStream fos = new FileOutputStream(clazz);
fos.write(cw0.toByteArray());
fos.close();
ManipulationProperty.getLogger().log(ManipulationProperty.INFO, "Put the file " + clazz.getAbsolutePath() + " in the jar file");
} catch (Exception e) {
System.err.println("Problem to write the adapted class on the file system " + " [ " + clazz.getAbsolutePath() + " ] " + e.getMessage());
}
}
// The file is in the bundle
return true;
}
/**
* Manipulate the given byte array.
*
* @param origin :
* original class.
* @return the manipulated class.
* @throws IOException :
* if an error occurs during the manipulation.
*/
public byte[] manipulate(byte[] origin) throws IOException {
InputStream is1 = new ByteArrayInputStream(origin);
// First check if the class is already manipulated :
ClassReader ckReader = new ClassReader(is1);
ClassChecker ck = new ClassChecker();
ckReader.accept(ck, ClassReader.SKIP_FRAMES);
is1.close();
m_fields = ck.getFields();
// Get interface and remove POJO interface is presents
String[] its = ck.getInterfaces();
List l = new ArrayList();
for (int i = 0; i < its.length; i++) {
l.add(its[i]);
}
l.remove("org/apache/felix/ipojo/Pojo");
m_interfaces = new String[l.size()];
for (int i = 0; i < m_interfaces.length; i++) {
m_interfaces[i] = ((String) l.get(i)).replace('/', '.');
}
for (int i = 0; i < ck.getMethods().size(); i++) {
MethodDescriptor method = (MethodDescriptor) ck.getMethods().get(i);
if (!(method.getName().startsWith("_get") || // Avoid getter method
method.getName().startsWith("_set") || // Avoid setter method
method.getName().equals("_setComponentManager") || // Avoid the set method
method.getName().equals("getComponentInstance"))) { // Avoid the getComponentInstance method
m_methods.add(method);
}
}
ClassWriter finalWriter = null;
if (!ck.isalreadyManipulated()) {
// Manipulation ->
// Add the _setComponentManager method
// Instrument all fields
InputStream is2 = new ByteArrayInputStream(origin);
ClassReader cr0 = new ClassReader(is2);
ClassWriter cw0 = new ClassWriter(ClassWriter.COMPUTE_MAXS);
PojoAdapter preprocess = new PojoAdapter(cw0);
cr0.accept(preprocess, ClassReader.SKIP_FRAMES);
is2.close();
finalWriter = cw0;
}
// The file is in the bundle
return finalWriter.toByteArray();
}
/**
* Compute component type manipulation metadata.
* @return the manipulation metadata of the class.
*/
public Element getManipulationMetadata() {
Element elem = new Element("Manipulation", "");
for (int j = 0; j < m_interfaces.length; j++) {
Element itf = new Element("Interface", "");
Attribute att = new Attribute("name", m_interfaces[j]);
itf.addAttribute(att);
elem.addElement(itf);
}
for (Iterator it = m_fields.keySet().iterator(); it.hasNext();) {
Element field = new Element("Field", "");
String name = (String) it.next();
String type = (String) m_fields.get(name);
Attribute attName = new Attribute("name", name);
Attribute attType = new Attribute("type", type);
field.addAttribute(attName);
field.addAttribute(attType);
elem.addElement(field);
}
for (int j = 0; j < m_methods.size(); j++) {
MethodDescriptor method = (MethodDescriptor) m_methods.get(j);
elem.addElement(method.getElement());
}
return elem;
}
}