Skip to content
IT-505 · Advanced Java Lab/Quick Revision Short Notes

Advanced Java Lab (IT-505) - Unit 4 Short Notes

UNIT 4: Advanced Java Lab - Short Notes

1.0 Java Servlet Technology

1.1 Introduction to Web Applications & Servlet Architecture

  • Web Application: A dynamic application accessed over a network (HTTP/HTTPS).

  • Servlet Container (Tomcat): Manages servlet lifecycle, handles requests/responses, provides JSP engine and other services.

  • Deployment Descriptor (web.xml): XML file in WEB-INF/ defining servlets, filters, listeners, security constraints, and initialization parameters.

  • Servlet Lifecycle:

    1. Loading & Instantiation: Container loads servlet class and creates instance.

    2. Initialization (init(ServletConfig config)): Called once. Used for one-time setup. ServletConfig provides init parameters.

    3. Request Handling (service(ServletRequest req, ServletResponse res)): For each request, container calls service(), which in HttpServlet dispatches to doGet(), doPost(), etc.

    4. Destruction (destroy()): Called before removing servlet from memory. For cleanup.

  • HTTP Basics:

    • Stateless Protocol: Each request is independent; server doesn't retain client info between requests.

    • Methods: GET (safe, idempotent, URL parameters), POST (non-idempotent, form data in body).

[!TIP] Exam Focus: Be prepared to draw the servlet lifecycle diagram and explain the role of web.xml.

1.2 Developing Servlets

  • HttpServlet Class: Abstract class extending GenericServlet. Override doGet(HttpServletRequest req, HttpServletResponse resp) and doPost().

  • Reading Request Data:

    • String getParameter(String name): For form data (both GET & POST).

    • String[] getParameterValues(String name): For multi-select checkboxes.

    • Enumeration<String> getParameterNames().

  • Sending Response:

    • PrintWriter getWriter(): For text (HTML, XML, JSON). Set content type via resp.setContentType("text/html").

    • ServletOutputStream getOutputStream(): For binary data (images, PDFs).

  • Initialization Parameters:

    • Servlet-level: In web.xml <init-param> inside <servlet>. Access via getServletConfig().getInitParameter("name").

    • Context-level (Application-wide): In web.xml <context-param>. Access via getServletContext().getInitParameter("name").

1.3 Session Management

Since HTTP is stateless, techniques to track a user across multiple requests:

Technique Mechanism Pros Cons
Cookies javax.servlet.http.Cookie sent in HTTP headers. Simple, persistent. User can disable, size limit (~4KB).
URL Rewriting Append session ID to URLs (;jsessionid=...). Works when cookies disabled. Messy URLs, must encode all links (response.encodeURL()).
Hidden Form Fields <input type="hidden"> in every form. Simple. Only works for POST from forms.
HttpSession Server-side object (request.getSession()). Secure, large storage, no client-side tampering. Consumes server memory, requires session ID tracking (via cookie/URL).
  • HttpSession API:

    • Creation: HttpSession session = request.getSession(); (creates if absent) or request.getSession(false) (returns null if absent).

    • Attribute Management: session.setAttribute("key", obj), session.getAttribute("key"), session.removeAttribute("key").

    • Invalidation: session.invalidate() (logs user out).

    • Configuration: session.setMaxInactiveInterval(seconds) in web.xml or programmatically.

1.4 Servlet Collaboration & Scope Objects

  • RequestDispatcher (for server-side forwarding/include):

    • Obtain: RequestDispatcher rd = request.getRequestDispatcher("target.jsp");

    • rd.forward(request, response): Transfers control to another resource. Request & Response objects are the same. Client URL unchanged.

    • rd.include(request, response): Includes content of another resource in the current response.

  • Scope Objects (Attribute Storage):

    | Scope | Class | Lifetime | Visibility | | :--- | :--- | :--- | :--- | | Request | ServletRequest | Until response is sent | Current request only | | Session | HttpSession | Until session invalidates/expires | User's session | | Application | ServletContext | Until container shuts down | Entire web app |

1.5 Exception Handling in Servlets

  • Declarative (in web.xml):

    
    <error-page>
    
        <error-code>404</error-code> <!-- or <exception-type>java.lang.Exception</exception-type> -->
    
        <location>/error.jsp</location>
    
    </error-page>
    
    
  • Programmatic: Use try-catch blocks in servlet code and forward to an error page manually.


2.0 JavaServer Pages (JSP)

2.1 JSP Architecture & Lifecycle

  • JSP vs Servlet: JSP is a view technology. First request triggers translation (JSP → Servlet .java) and compilation (.java → .class). Subsequent requests execute the compiled servlet directly.

  • Lifecycle Phases:

    1. Translation: JSP page converted to servlet source code.

    2. Compilation: Source code compiled to bytecode.

    3. Execution: Servlet's _jspService() handles requests.

    4. Cleanup: jspDestroy() called on shutdown.

2.2 JSP Elements

  • Directives (<%@ ... %>): Global settings.

    • <%@ page language="java" contentType="text/html" %> (page-level).

    • <%@ include file="header.html" %> (static include at translation time).

    • <%@ taglib uri="..." prefix="c" %> (import tag library).

  • Scripting Elements (discouraged in modern JSP; use EL/JSTL instead):

    • Scriptlet <% ... %>: Java code in _jspService().

    • Expression <%= ... %>: Evaluates and prints result.

    • Declaration <%! ... %>: Declares methods/fields at servlet class level.

  • Implicit Objects (available in JSP without declaration):

    out (JspWriter), request, response, session, application (ServletContext), config (ServletConfig), pageContext (unified scope access), page (this), exception (in error pages).

2.3 JSP Standard Tag Library (JSTL)

  • Core Tags (c:):

    | Tag | Purpose | | :--- | :--- | | <c:out value="${...}"/> | Print escaped value (prevents XSS). | | <c:set var="x" value="..." scope="..."/> | Set scoped attribute. | | <c:remove var="x" scope="..."/> | Remove attribute. | | <c:if test="${...}">...</c:if> | Conditional. | | <c:choose><c:when test="...">...</c:when><c:otherwise>...</c:otherwise></c:choose> | Switch-case. | | <c:forEach var="item" items="${list}">...</c:forEach> | Loop over collections/arrays. | | <c:import url="..."/> | Include content from another resource (dynamic). | | <c:url value="..."/> | Rewrites URL (adds session ID if needed). |

  • Formatting Tags (fmt:):

    • <fmt:formatNumber value="${num}" type="currency"/>

    • <fmt:formatDate value="${date}" pattern="dd/MM/yyyy"/>

    • <fmt:setLocale value="en_US"/>

  • SQL Tags (sql:) (Learning only): <sql:query>, <sql:update>, <sql:param>.

2.4 Expression Language (EL)

  • Syntax: ${expression}. Evaluates to a value.

  • Implicit Objects:

    | Category | Objects | | :--- | :--- | | Scope | pageScope, requestScope, sessionScope, applicationScope | | Request | param, paramValues (form data), header, headerValues | | Context | initParam (context-param), cookie |

  • Operators: Arithmetic (+, -, *, /, %), Relational (==, !=, <, >, <=, >=), Logical (&&, ||, !, empty), Ternary (?:).

  • Accessing Properties: $$\displaystyle {user.name}` calls `user.getName()` (JavaBean convention). For Map/List: ` $${map['key']}, ${list[0]}.

2.5 Custom Tags

  • Need: Encapsulate complex logic, promote reuse, separate from JSP page logic.

  • Simple Tag Handlers: Extend javax.servlet.jsp.tagext.SimpleTagSupport. Implement doTag().

  • Tag Files (.tag): Simpler alternative to Java classes. Written in JSP-like syntax. Placed in /WEB-INF/tags/.


3.0 Java Database Connectivity (JDBC) - Advanced Usage

3.1 JDBC Architecture & Driver Types

  • Architecture: Application → JDBC API → JDBC Driver Manager → JDBC Driver → Database.

  • Driver Types:

    1. Type 1 (JDBC-ODBC Bridge): JDBC calls to ODBC. (Deprecated).

    2. Type 2 (Native-API): JDBC calls to database-specific native API.

    3. Type 3 (Network-Protocol): JDBC to middleware server.

    4. Type 4 (Thin Driver): Pure Java, directly with DB. Most common (e.g., MySQL Connector/J).

3.2 Connection Management

  • DriverManager: Basic, creates a new physical connection for each call. Not suitable for production web apps.

  • DataSource (Connection Pooling): Recommended for web apps. Manages a pool of reusable connections.

    • Configuration: Done in servlet container (Tomcat's context.xml or server.xml) or via libraries (Apache DBCP, HikariCP).

    • Lookup: DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/MyDB");

    • Get Connection: Connection conn = ds.getConnection(); (Returns a pooled connection).

3.3 Advanced Statement & ResultSet Handling

  • PreparedStatement:

    • Precompiled SQL with ? placeholders.

    • Benefits: Prevents SQL Injection, better performance for repeated execution.

    • Set parameters: ps.setInt(1, userId);, ps.setString(2, name);.

    • Batch Updates: ps.addBatch(); then ps.executeBatch();.

  • CallableStatement: For stored procedures. {call proc_name(?, ?)}.

  • ResultSet Types & Concurrency:

    • Type: TYPE_FORWARD_ONLY (default), TYPE_SCROLL_INSENSITIVE, TYPE_SCROLL_SENSITIVE.

    • Concurrency: CONCUR_READ_ONLY (default), CONCUR_UPDATABLE.

    • Create: stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);

  • RowSet:

    • Connected (JdbcRowSet): Always connected to DB.

    • Disconnected (CachedRowSet): Can disconnect, serialize, work offline, then reconnect to sync.

3.4 Transaction Management

  • ACID Properties:

    • Atomicity: All or nothing.

    • Consistency: DB moves from one valid state to another.

    • Isolation: Concurrent transactions don't interfere.

    • Durability: Committed changes persist.

  • JDBC Transaction Control (Auto-commit is true by default):

    
    conn.setAutoCommit(false); // Start transaction
    
    try {
    
        // ... execute statements ...
    
        conn.commit(); // Success
    
    } catch (SQLException e) {
    
        conn.rollback(); // Failure
    
    } finally {
    
        conn.setAutoCommit(true); // Reset
    
    }
    
    
  • Savepoints: Savepoint sp = conn.setSavepoint("mid");, conn.rollback(sp);.

3.5 JDBC in Web Applications

  • DAO (Data Access Object) Pattern:

    • Model: POJOs/Entities.

    • DAO Interface: CRUD methods (create(), read(), update(), delete()).

    • DAO Implementation: Contains all JDBC code. Isolates DB logic from business logic.

  • MVC Integration:

    • Servlet (Controller): Calls DAO methods, stores results in request/session scope, forwards to JSP.

    • JSP (View): Uses EL/JSTL to display data from scoped attributes.

    • DAO/Model: Handles data persistence.


4.0 Model-View-Controller (MVC) Architecture in Java Web Apps

4.1 Understanding MVC Pattern

  • Model: Business logic & data (POJOs, DAOs, Services). Independent of UI.

  • View: Presentation layer (JSP, HTML). Displays data from Model. Minimal logic.

  • Controller: Mediator. Receives user input (from View), interacts with Model, selects View.

  • Flow:

    1. User interacts with View (clicks link/submits form).

    2. Request goes to Controller (Servlet).

    3. Controller invokes Model (DAO/Service) to process data.

    4. Model returns data/status to Controller.

    5. Controller selects View (JSP) and forwards request with data.

    6. View renders response using data.

4.2 Implementing MVC with Servlets & JSP

  • Front Controller Pattern: A single servlet (e.g., ControllerServlet) handles all requests, dispatches to appropriate business logic. (Simpler than one servlet per action).

  • JSP as Pure View: Use EL (${user.name}) and JSTL (<c:forEach>). Avoid scriptlets (<% %>).

  • JavaBeans as Model: Simple POJOs with private fields, no-arg constructor, getters/setters.

  • Data Flow Example:

    • UserServlet (Controller) → UserDAO.findById(id) (Model) → request.setAttribute("user", user) → forward("userDetails.jsp") (View).

5.0 Introduction to Spring Framework (Core & Web)

5.1 Spring Framework Overview

  • Core Concept: Inversion of Control (IoC): Object creation & dependency management is inverted from application to container (Spring).

  • Dependency Injection (DI): A form of IoC. Dependencies are "injected" into objects by the container (via constructor/setters).

  • Spring Container:

    • BeanFactory: Basic container, lazy loading.

    • ApplicationContext: Advanced, eager loading, supports internationalization, events. Most used.

5.2 Bean Configuration

  • XML-Based (beans.xml):

    
    <bean id="myBean" class="com.example.MyClass">
    
        <property name="propertyName" value="value"/>
    
    </bean>
    
    
  • Annotation-Based (Component Scanning):

    • Stereotype Annotations: @Component (generic), @Service (business layer), @Repository (DAO layer), @Controller (web layer).

    • Dependency Injection: @Autowired (by type), @Qualifier("beanName") (disambiguate), @Value("${property}") (inject values from properties).

    • Java Config: @Configuration class with @Bean methods.

      
      @Configuration
      
      public class AppConfig {
      
          @Bean
      
          public MyService myService() {
      
              return new MyServiceImpl();
      
          }
      
      }
      
      

5.3 Spring MVC Framework

  • Front Controller: DispatcherServlet (defined in web.xml or via SpringBootServletInitializer). Maps *.do or /.

  • Request Mapping: @Controller classes with handler methods.

    • @RequestMapping("/users") at class level.

    • @GetMapping("/{id}"), @PostMapping, @PutMapping, @DeleteMapping.

  • Controller Methods:

    • Parameters: @RequestParam, @PathVariable, @RequestBody, Model, HttpServletRequest, etc.

    • Return: String (view name), ModelAndView, @ResponseBody (direct response body).

  • View Resolution: InternalResourceViewResolver configured in Spring config to map view names to JSPs (/WEB-INF/views/.jsp`).

5.4 Spring RESTful Web Services

  • @RestController: Combination of @Controller + @ResponseBody. All methods return JSON/XML directly.

  • Consuming/Producing:

    • @RequestBody: Deserialize request body (JSON/XML) to Java object.

    • @ResponseBody: Serialize return object to response body.

    • @RequestMapping(consumes="application/json", produces="application/json").

  • RestTemplate (Client): Spring's synchronous HTTP client.

    
    RestTemplate rt = new RestTemplate();
    
    User user = rt.getForObject("http://localhost:8080/api/users/1", User.class);
    
    

5.5 Spring Boot Essentials

  • Core Idea: Convention over configuration. Auto-configures beans based on classpath.

  • Starter Dependencies: spring-boot-starter-web (includes Tomcat, Spring MVC, Jackson), spring-boot-starter-data-jpa, etc.

  • Embedded Server: Tomcat/Jetty/Undertow runs inside the app JAR. No external WAR deployment needed.

  • application.properties:

    
    server.port=8081
    
    spring.datasource.url=jdbc:mysql://localhost:3306/db
    
    spring.jpa.hibernate.ddl-auto=update
    
    
  • Main Class:

    
    @SpringBootApplication // @Configuration + @EnableAutoConfiguration + @ComponentScan
    
    public class App {
    
        public static void main(String[] args) {
    
            SpringApplication.run(App.class, args);
    
        }
    
    }
    
    

6.0 Hibernate Framework (ORM)

6.1 ORM Concepts & Hibernate Architecture

  • Problem with JDBC: Impedance mismatch (objects vs. tables), boilerplate code, manual relationship handling.

  • ORM (Object-Relational Mapping): Maps Java objects to database tables.

  • Hibernate Core Interfaces:

    • SessionFactory: Immutable, thread-safe, built from Configuration. Expensive to create.

    • Session: Single-threaded, short-lived (per request/transaction). Main interface for CRUD.

    • Transaction: session.beginTransaction(), tx.commit(), tx.rollback().

    • Query: For HQL/Criteria.

6.2 Entity Mapping

  • POJO as Entity:

    
    @Entity
    
    @Table(name="users")
    
    public class User {
    
        @Id // Primary key
    
        @GeneratedValue(strategy=GenerationType.IDENTITY)
    
        private Long id;
    
        @Column(name="user_name", nullable=false, length=50)
    
        private String username;
    
        @Transient // Not persisted
    
        private int tempScore;
    
        // getters/setters
    
    }
    
    
  • Access Type: Field-based (annotations on fields) vs. Property-based (on getters). Consistent within an entity.

6.3 Associations & Relationships

  • @OneToOne: @JoinColumn on owning side.

  • @OneToMany / @ManyToOne:

    • Bidirectional: @OneToMany(mappedBy="user") (inverse), @ManyToOne (owning side with @JoinColumn).

    • Unidirectional: @OneToMany with @JoinColumn (owning side).

    • Cascade: cascade=CascadeType.ALL (persist, merge, remove, etc.).

  • @ManyToMany:

    • Requires join table: @JoinTable(name="user_role", joinColumns=@JoinColumn(name="user_id"), inverseJoinColumns=@JoinColumn(name="role_id")).

    • Usually bidirectional with mappedBy on one side.

6.4 Hibernate Query Languages

  • HQL/JPQL: Object-oriented query language (operates on entities, not tables).

    
    List<User> users = session.createQuery("FROM User WHERE username = :uname", User.class)
    
                             .setParameter("uname", "john")
    
                             .list();
    
    
  • Criteria API (JPA): Type-safe, programmatic query building.

    
    CriteriaBuilder cb = session.getCriteriaBuilder();
    
    CriteriaQuery<User> cq = cb.createQuery(User.class);
    
    Root<User> root = cq.from(User.class);
    
    cq.select(root).where(cb.equal(root.get("username"), "john"));
    
    List<User> users = session.createQuery(cq).list();
    
    
  • Native SQL: session.createNativeQuery("SELECT * FROM users", User.class).

6.5 Integrating Hibernate with Spring

  • Using JPA (EntityManager):

    • Configuration: LocalContainerEntityManagerFactoryBean (wraps EntityManagerFactory).

    • Transaction Manager: JpaTransactionManager.

    • Repository: JpaRepository (Spring Data JPA) for auto CRUD.

  • Declarative Transactions:

    
    @Service
    
    public class UserService {
    
        @Transactional // Spring manages tx begin/commit/rollback
    
        public void transfer(Long fromId, Long toId, double amount) {
    
            // ... DAO calls ...
    
        }
    
    }
    
    

7.0 Web Services & APIs (SOAP/REST)

7.1 RESTful Web Services Principles

  • Resource: Any information/entity (e.g., /users).

  • URI: Unique identifier for resource. Should be noun-based (/users, not /getUsers).

  • HTTP Verbs:

    | Verb | CRUD | Idempotent | Safe | | :--- | :--- | :--- | :--- | | GET | Read | Yes | Yes | | POST | Create | No | No | | PUT | Update (full) | Yes | No | | PATCH | Update (partial) | No | No | | DELETE | Delete | Yes | No |

  • Stateless: Each request contains all info needed. No server-side session.

  • Representation: Resource can have multiple representations (JSON, XML). Accept and Content-Type headers negotiate.

7.2 Building REST APIs with JAX-RS (or Spring REST)

  • JAX-RS (Jersey/RESTEasy) Annotations:

    • @Path("/users") on class.

    • @GET, @POST, @PUT, @DELETE on methods.

    • @Path("{id}") on method for path param.

    • @QueryParam("name"), @FormParam("email").

    • @Consumes(MediaType.APPLICATION_JSON), @Produces(MediaType.APPLICATION_JSON).

    • Return Response for custom status/headers: Response.status(201).entity(user).build().

  • Spring REST (see 5.4): Uses @RestController, @GetMapping, etc.

7.3 Consuming REST APIs

  • HttpURLConnection (Standard Java):

    
    URL url = new URL("http://api.example.com/users");
    
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    
    conn.setRequestMethod("GET");
    
    // Read InputStream...
    
    
  • RestTemplate (Spring): Simplifies with getForObject(), postForObject(), exchange().

  • JAX-RS Client API: ClientBuilder.newClient().target("...").request().get().

7.4 SOAP Web Services Basics (Optional)

  • WSDL (Web Services Description Language): XML file describing service (operations, messages, bindings).

  • SOAP Envelope: <soap:Envelope> containing <soap:Header> (optional) and <soap:Body>.

  • JAX-WS Annotations (for SOAP web services):

    • @WebService on service endpoint interface/class.

    • @WebMethod on methods.

    • @WebParam on parameters.


8.0 Build Tools & Deployment

8.1 Apache Maven

  • pom.xml (Project Object Model):

    • groupId, artifactId, version.

    • <dependencies>: Libraries (Servlet API, JSP, Spring, Hibernate, MySQL Connector). Scope provided for servlet-api.

    • <build>: Plugins (maven-war-plugin, tomcat7-maven-plugin for embedded Tomcat).

  • Standard Directory Layout:

    
    src/main/java/     (source code)
    
    src/main/resources/ (config files)
    
    src/main/webapp/   (WEB-INF/, JSPs, static files)
    
    src/test/java/
    
    
  • Common Commands:

    • mvn clean: Delete target/ directory.

    • mvn compile: Compile source.

    • mvn package: Create WAR file in target/.

    • mvn install: Install WAR to local repository.

    • mvn tomcat7:run: Run embedded Tomcat (with plugin).

8.2 Deployment on Apache Tomcat

  • WAR Deployment: Copy app.war to $CATALINA_HOME/webapps/. Tomcat auto-explodes to webapps/app/.

  • Context Path: Name of WAR file (app.war → http://localhost:8080/app). Customize in META-INF/context.xml or $CATALINA_HOME/conf/[engine]/[host]/app.xml.

  • Server Configuration:

    • server.xml: Connector (port), <Host> (appBase), <Context>.

    • context.xml: Default context settings for all apps.

  • Logging:

    • catalina.out: Stdout/stderr of Tomcat.

    • Application logs: Configure log4j.properties or logback.xml in WEB-INF/classes.


9.0 Security Considerations (Basic)

9.1 Web Application Security Threats

Threat Description Mitigation
SQL Injection Malicious SQL via input fields. Always use PreparedStatement.
XSS (Cross-Site Scripting) Inject client-side scripts. Output encode (<c:out>), Content Security Policy (CSP).
CSRF (Cross-Site Request Forgery) Force user to execute unwanted actions. CSRF tokens in forms (Spring Security provides).
Session Fixation Attacker sets user's session ID. Invalidate old session on login (session.invalidate()), create new.
Insecure Direct Object Reference Access objects without authorization check. Always validate user has permission for requested resource.

9.2 Secure Coding Practices

  • Input Validation: Validate on server-side (client-side can be bypassed). Use whitelisting (allow known good).

  • Output Encoding: Encode data based on context (HTML, JavaScript, SQL). Use JSTL <c:out> for HTML.

  • Password Storage: Never store plain text. Use strong, salted hashing:

    • BCrypt (recommended): BCryptPasswordEncoder (Spring Security), jBCrypt library.

    • PBKDF2, SCrypt, Argon2.

  • Secure Session Management:

    • Use HttpOnly and Secure flags for cookies (HTTPS only).

    • Set reasonable session timeout (<session-config><session-timeout> in web.xml).

    • Regenerate session ID on login/privilege change.

9.3 Spring Security Fundamentals

  • Core Concepts:

    • Authentication: Who are you? (Username/Password, OAuth, etc.)

    • Authorization: What can you do? (Roles/Permissions).

  • Basic Configuration (Java Config):

    
    @Configuration
    
    @EnableWebSecurity
    
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
    
        protected void configure(HttpSecurity http) throws Exception {
    
            http
    
                .authorizeRequests()
    
                    .antMatchers("/admin/**").hasRole("ADMIN") // URL-based auth
    
                    .antMatchers("/user/**").hasAnyRole("USER","ADMIN")
    
                    .anyRequest().authenticated()
    
                .and()
    
                .formLogin() // Default login page
    
                .and()
    
                .logout().permitAll();
    
        }
    
        @Override
    
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
            auth
    
                .inMemoryAuthentication()
    
                .withUser("user").password(passwordEncoder().encode("pass")).roles("USER");
    
        }
    
        @Bean
    
        public PasswordEncoder passwordEncoder() {
    
            return new BCryptPasswordEncoder();
    
        }
    
    }
    
    
  • UserDetailsService: Interface to load user-specific data (usually from DB) for authentication.

  • Method Security: @PreAuthorize("hasRole('ADMIN')") on service methods. Enable with @EnableGlobalMethodSecurity(prePostEnabled = true).


\boxed{\text{End of UNIT 4 Notes}}

Go to where you left off?

Quick Add to Notes

Save questions, your own notes and screenshots into notes filed by unit. It takes a free account.

Create free account

Have an account? Log in