Java EE Error: javax.enterprise.inject.UnsatisfiedResolutionException: Api type

Irfaan picture Irfaan · May 24, 2016 · Viewed 9.5k times · Source

I'm in the process of building a simple webapp modelled on the MVC architecture. I have a little JSP page with an HTML form. The form data gets forwarded to a servlet which then delegates to the Controller class.

When clicking submit on the form I get a HTTP Status 500 - Error caused by an "UnsatisfiedResolutionException". It seems the container can't find the Controller?

A Context.xml file serves as my Data Source. I'm using apache tomee webprofile 1.7.4.

The error:

type Exception report

message Error instantiating servlet class a1.DispatcherServlet

description The server encountered an internal error that prevented it from fulfilling this request.

exception

javax.servlet.ServletException: Error instantiating servlet class a1.DispatcherServlet
    org.apache.tomee.catalina.OpenEJBValve.invoke(OpenEJBValve.java:44)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:436)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1078)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    java.lang.Thread.run(Thread.java:745)
root cause

javax.enterprise.inject.UnsatisfiedResolutionException: Api type [a1.Controller] is not found with the qualifiers 
Qualifiers: [@javax.inject.Named(value=customerController)]
for injection into Field Injection Point, field name :  customerController, Bean Owner : [DispatcherServlet, Name:null, WebBeans Type:DEPENDENT, API Types:[java.io.Serializable,javax.servlet.GenericServlet,java.lang.Object,a1.DispatcherServlet,javax.servlet.Servlet,javax.servlet.http.HttpServlet,javax.servlet.ServletConfig], Qualifiers:[javax.enterprise.inject.Any,javax.enterprise.inject.Default]]
    org.apache.webbeans.util.InjectionExceptionUtil.throwUnsatisfiedResolutionException(InjectionExceptionUtil.java:60)
    org.apache.webbeans.container.InjectionResolver.getInjectionPointBean(InjectionResolver.java:250)
    org.apache.webbeans.inject.AbstractInjectable.inject(AbstractInjectable.java:76)
    org.apache.webbeans.inject.InjectableField.doInjection(InjectableField.java:65)
    org.apache.webbeans.portable.InjectionTargetImpl.injectFields(InjectionTargetImpl.java:208)
    org.apache.webbeans.portable.InjectionTargetImpl.inject(InjectionTargetImpl.java:194)
    org.apache.webbeans.portable.InjectionTargetImpl.inject(InjectionTargetImpl.java:184)
    org.apache.webbeans.component.AbstractOwbBean.create(AbstractOwbBean.java:125)
    org.apache.openejb.core.WebContext.newInstance(WebContext.java:138)
    org.apache.tomee.catalina.JavaeeInstanceManager.newInstance(JavaeeInstanceManager.java:46)
    org.apache.tomee.catalina.JavaeeInstanceManager.newInstance(JavaeeInstanceManager.java:66)
    org.apache.tomee.catalina.JavaeeInstanceManager.newInstance(JavaeeInstanceManager.java:61)
    org.apache.tomee.catalina.OpenEJBValve.invoke(OpenEJBValve.java:44)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:436)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1078)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    java.lang.Thread.run(Thread.java:745)

DispatcherServlet.java:

package a1;

/* Imports */

public class DispatcherServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Inject
    @Named("customerController")
    private Controller customerController;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            String methodName = "addCustomer";

            Method method = customerController.getClass().getDeclaredMethod(methodName, HttpServletRequest.class);
            method.setAccessible(true);
            method.invoke(customerController, req);

            req.getRequestDispatcher("/success.jsp").forward(req, resp);
        } catch (SecurityException | IllegalArgumentException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}

CustomerController.java:

package a1;

/* Imports */

@Named("customerController")
public class CustomerController implements Controller {

    @EJB
    private CustomerService customerServiceImpl;

    private void addCustomer(HttpServletRequest req) {
        long cn1 = Long.parseLong(req.getParameter("form_cust_no"));
        String n = req.getParameter("form_name");
        String s = req.getParameter("form_surname");

        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            String formDate = req.getParameter("form_dob");
            java.util.Date dateUtil = sdf.parse(formDate);
            java.sql.Date dateSql = new java.sql.Date(dateUtil.getTime());
            Customer customer = new Customer(cn1, n, s, dateSql);

            customerServiceImpl.addCustomer(customer);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

Web.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0" metadata-complete="true">

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>a1.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/dispatcher/*</url-pattern>
    </servlet-mapping>

    <resource-ref>
        <res-ref-name>jdbc/MyDataSource</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>
</web-app>

Persistence.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1" xsi:schemalocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="Ejb1" transaction-type="JTA">
        <jta-data-source>jdbc/MyDataSource</jta-data-source>

        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="hibernate.connection.autocommit" value="false" />
        </properties>
    </persistence-unit>
</persistence>

Beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

Build.xml

<?xml version="1.0"?>
<project name="h6" default="all" basedir=".">
    <description>Homework 6</description>

    <property file="build.properties" />

    <path id="master-classpath">
        <fileset dir="C:/myprograms/apache-tomee-webprofile-1.7.4/lib" />
        <pathelement path="/a1/" />
    </path>

    <target name="clean" description="Clean class files and war files">
        <delete file="${targetdeploy.dir}${appname}.war" failonerror="false" />
        <delete file="${server.dir}/${appname}.war" failonerror="false" />
        <delete dir="${server.dir}/${appname}" failonerror="false" />
        <delete dir="/${classpath}/" failonerror="false" />
    </target>

    <target name="compile" depends="clean" description="Compile all files into target">
        <javac srcdir="./src/main/java" destdir="./${classpath}" includeAntRuntime="false">
            <classpath refid="master-classpath" />
        </javac>
    </target>

    <target name="copy-classes" depends="compile" description="Copy classes from target into webapp">
        <copy todir="src/main/webapp/WEB-INF/classes/">
            <fileset dir="${classpath}" includes="**/*.class" />
            <fileset dir="${classpath}" includes="**/*.xml" />
        </copy>
        <copy todir="src/main/webapp/WEB-INF/lib/">
            <fileset dir="${classpath}" includes="**/*.jar" />
        </copy>
    </target>

    <target name="generate-war" depends="copy-classes" description="Generate war file from webapp contents">
        <war destfile="./${targetdeploy.dir}${appname}.war" webxml="src/main/webapp/WEB-INF/web.xml">
            <fileset dir="src/main/webapp/">
                <include name="**/*.class" />
                <include name="**/*.jsp" />
                <include name="**/*.css" />
                <include name="**/*.html" />
                <include name="**/*.txt" />
                <include name="**/*.properties" />
                <include name="**/*.jar" />
                <include name="**/*.xml" />
            </fileset>
        </war>
    </target>

    <target name="deploy-war" depends="generate-war" description="Copy war file into server">
        <copy todir="${server.dir}/">
            <fileset dir="${targetdeploy.dir}">
                <include name="*.war" />
            </fileset>
        </copy>
    </target>

    <target name="extract-war" depends="deploy-war">
        <mkdir dir="${server.dir}/${appname}" />
        <unwar src="${server.dir}/${appname}.war" dest="${server.dir}/${appname}" />
    </target>

    <target name="all" depends="extract-war" />
</project>

Build.properties:

server.dir=C:/myprograms/apache-tomee-webprofile-1.7.4/webapps
classpath=target/ide/classes
targetdeploy.dir=target/deploy/
appname=h6

Folder structure:

enter image description here

Answer

Jonathan S. Fisher picture Jonathan S. Fisher · May 24, 2016

I would not recommend injecting based on Strings like you have in this example; your code actually works just fine for me, so something else in your project is wrong is causing the problem that you did not post. You can avoid these problems by using compiled Qualifiers rather than String based ones.

So instead of this:

@Named("customerController")
public class CustomerController implements Controller {

....

@Inject
@Named("customerController")
private Controller customerController;

Simply do this: (if you have only one implementation of Controller)

@Inject
private Controller customerController;

....

@ApplicationScoped
public class CustomerController implements Controller {

And you're done. You did not post the code to Controller, or if there were multiple implementations of Controller. Assuming you do have multiple implementations, there are a ton of ways you can handle it, depending on your requirements.

Easiest:

@Inject
private CustomerController customerController;

More Flexible:

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE})
public @interface Customer {
}

...

@Inject
@Customer
private Controller customerController;

...

@ApplicationScoped
@Customer
public class CustomerController implements Controller {

Or perhaps you only have multiple implementations, but you need to change the implementation every once in awhile application wide:

@Inject
private Controller customerController;

...

@ApplicationScoped
@Default
public class CustomerController implements Controller {

...

@ApplicationScoped
@Alternative
public class AnotherCustomerController implements Controller {

...

Then to activate the alternative:

<beans ... >
    <alternatives>
        <class>a1.AnotherCustomerController</class>
    </alternatives>
</beans>

Good luck! Post replies back with further questions or followup!

PS:

I used @ApplicationScoped instead of @Dependent because your code appears to have no shared state.