Tier — Based on OJB XML Configuration Service Manager Services Filters XML Configuration DTO’s XML Configuration Data Repository JDBC JSP Views Actions SQL Statements
the domain object; 2. Write the SQL schema to store the domain object; 3. Write the xml configuration to map the POJO against the SQL schema; 4. Write SQL statements for custom reading of the domain object (in case something other than a read by ID or a read all was necessary); 5. Create data transfer objects for passing information to the presentation tier; 6. Write services at the application level for creating, reading, updating and deleting the domain object; 7. Write access control filters to limit who can invoke services; 8. Write service and filter configurations in a xml file; 9. Create JSP views for presenting information to the users; 10. Write Actions to handle user requests; 11. Configure struts xml files to specify the application flow.
— Fenix Framework Based Service Manager Services Filters XML Configuration Domain Objects XML Configuration Data Repository JDBC JSP Views Actions DML Configuration
persistent, rich domain models • Transparent persistency, strong consistency guarantees across multiple concurrent actors • Designed for modularity • No commitment with a particular backend • More Info • Confluence • Fenix Framework Page
Serializable, JSON, Enums • User-defined: Any type • Multiple aliases for the same type • Must externalise to one or more value types • MUST be immutable valueType java.util.Locale as Locale { externalizeWith { String toLanguageTag(); } internalizeWith forLanguageTag(); } OR valueType java.util.Locale as JsonLocale { externalizeWith { String getLanguage(); String getCountry(); } }
• Each role must have a name (unique for that type) • Multiplicity ranges • E.g.: 0..1, 1..*, 5..9 • Upper multiplicity determines relation type (to-one, to-many) relation SystemUsers { protected Bennu playsRole bennu { multiplicity 1..1; } protected User playsRole user { multiplicity *; } }
is modified • Useful for ensuring constraints are maintained • Allows notifications before add, after add, before remove and after remove. static { Registration. getRelationShiftStudent(). addListener( new ShiftStudentListener()); }
They are re-generated for each module, and depend on the underlying backend • Undefined behaviour if invoked outside a transactional context (Hint: NPE) • Each DomainObject has a unique External ID • Object equality determined by identity (i.e. equals 㱻 ==) • Serializable, stores the object’s ID
within a transaction • Three different types of transactions: • Read-Only - Any write will throw a WriteOnReadError • Speculative Read - Write causes restart in full write • Write • Allows listeners and introspectors • Provide transaction-local storage
Manually - Begin, Commit, Rollback • FenixFramework.getTransactionManager().withTransaction(Callable<T>) • @Atomic methods • withTransaction or @Atomic preferred, as they handle conflicts by restarting the transaction, and take care of proper bookkeeping
is as simple as placing a file fenix-framework.properties on your final application • When it is first needed, the Framework will read the configuration file and start up automatically, taking all of the project’s dependencies as part of the domain
all its classes and relations. How can you get a reference to it? • The framework defines a special DomainRoot class. Invocations of FenixFramework.getDomainRoot() are guaranteed to always return the same instance DomainRoot ApplicationRoot A B
your application? • DomainObject.getExternalId() Returns a (unique) String representation of the object • FenixFramework.getDomainObject(String) Recovers the object by its ID. Results undefined if ID not returned by a call to getExternalId() • FenixFramework.isDomainObjectValid(DomainObject) determines whether the given object is still valid (i.e. is a proper reference, has not been deleted, etc)
can leave the domain in an inconsistent state • Programmer is responsible for specifying constraints on the domain • Two ways to specify Consistency Predicates
public final boolean namesCorrectlyPartitioned() { final String fullName = getName(); final String familyName = getFamilyNames(); final String composedName = getGivenNames() + " " + familyName; return fullName.equals(composedName); } } public class User { protected String username (REQUIRED); protected String password; (…) } public class User_Base extends AbstractDomainObject { @ConsistencyPredicate public final boolean checkMultiplicityOfUsername() { return getUsername() != null; } }
and Persistence Support • You should NEVER write code that depends on backends, as it is bound to change drastically, even between minor versions • Two kinds of backends: • In-memory: Useful for testing • Persistence backed: For production usage
• Based on the concept of Versioned Boxes, containing the history of an object’s value • Requires that every stored value is immutable • Changes can only occur within write transactions
of an application • Each Domain Object is stored as a row in the database • Tables organised per type hierarchy • Special FF$ tables are used for bookkeeping and metadata (Specially FF$DOMAIN_CLASS_INFO, which contains class information) • MySQL/MariaDB supported types are used natively when possible (i.e., time, blob)
the entire application • Dynamically change the theme of your application • Dynamic menu construction and rendering • Out of the box scaffolding for web applications
Dynamic model built from declared functionalities • This allows for full customisation of the menu • Semantic URLs from the path in the tree • Catch: The same functionality can only be installed in a single place
(Controller) • Actions are responsible for handling the request, and forwarding to the response • Typically another action or a JSP • Originally configured using XML • Extended for annotation-based configuration
declared in the module’s XML file • In the @Mapping, you can choose the form for your action • Forms are populated from request parameters, and validated according to specific rules • Parameters are accessible in a map-like interface DON’T USE!
@StrutsFunctionality • “Security” features - A checksum is injected into every link in the response, and checked when the functionality is accessed • Parsers for multipart requests • Synchronisation between Struts and application locales
bundle for each locale. Each file contains a list of strings internationalised for that locale. • Don’t write text directly in the JSPs. • Use the Resource Bundle Editor
@GET @Path("/{oid}") @Produces(MediaType.APPLICATION_JSON) public Response getUser(@PathParam("oid") String externalId) { return Response.ok(view(readDomainObject(externalId))).build(); } } • Endpoint available @ /api/bennu-core/users • Endpoints can be defined in any module. • What if oid doesn’t exist / not valid ? • readDomainObject -> throws exception with 404 (not found) status response
Java language in its history. A relatively small number of features (..) combine to offer a programming model that fuses the object-oriented and functional styles. Java Language Specification, Java SE 8
Type annotations Improved type inference Method parameter reflection Streams API HashMap improvements New monitoring tools Nashorn Java Mission Control New DateTime API LongAdders Optionals
These allow for API evolution without breaking backwards- compatibility default boolean removeIf(Predicate<? super E> f) { Objects.requireNonNull(f); (…) return removed; }
supporting sequential and parallel aggregate operations” • They provide fluent views of data streams (typically from collections) • Most operations are lazy people.stream().parallel(). map(Person::getName). filter(s -> s.startsWith(“João")). count(); people.forEach(p -> System.out.println(p) );
a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framework at deployment time.
context for your logger (Typically its enclosing type) • Be careful when choosing the log level • Always use SLF4J formatter, this avoids allocating unnecessary strings
free - Use it! • Each clone is a full copy, you can do whatever you want • A ton of amazing tools: rebase, squash, amend, commit reordering, collaboration
to a commit • You can freely play around with it, you can even move some commits to another point in the branch • Two ways of merging: • Fast-Forward: Simply moving the branch pointer • Creating a merge node, which points to both heads
a feature branch • Once you start working, create a new branch from develop and give it a proper name (e.g. feature/ spaces-refactor) • Commit all your changes to your branch • Push it to your fork
We will not accept messy history due to bad rebases/merges • Always pay attention to the version in each branch • Keep track of all your remotes • Use git fetch + git rebase at all times, or git pull --rebase
comprehension tool.” • Much more than a build tool • Based around the concept of project • Convention over configuration • Each project has it’s own POM file
ID (we use org.fenixedu), version and packaging. • May declare a parent project (to avoid duplicating common configuration). We provide fenixedu- project, fenix-framework-project and web-app-project. • May declare additional plugins for various tasks • Declare the project’s dependencies - No binaries in the SCM!
commit POMs with SNAPSHOT dependencies • Think twice before adding any dependencies • Java 8 and Guava provide many common tools • Don’t add dependencies that you don’t need • Install the Eclipse Maven Plugin (m2e)
our repositories. • We will not format the code for you. Bad formatting is a rejection reason, period. • Use the EclipseFenixCodeStyle.xml file • Works on other IDEs
and us. • You’re not in Kansas anymore. You most likely never worked on a large group or a real life project. Your error will cost someone time. • Everyone screws up sometime. • Refer to this talk when you are doing administrative tasks (committing code, closing issues). FenixEdu Team O nly
tracks everything from Academic Administrative Office requests from bugs. • RT Tickets are issued to you either by the Senior Staff or User Support Group. • After solving the ticket, give it back to who gave you. FenixEdu Team O nly
External errors are managed in JIRA Issues • Internal errors SHOULD give rise to external errors when new functionalities are created or bugs are fixed FenixEdu Team O nly
originating from RT, you MUST NOT include private data (ISTId, OIDs, Names, Numbers, etc.) • You can create a Issue that someone outside the FenixEdu team solves. • You can create more than one Issue for a RT ticket. Learn to divide your problem in sub-problems. FenixEdu Team O nly
the Senior Staff. • It is your responsibility to create a JIRA Issue regarding that functionality if one doesn’t exist, and close it when the functionality is done. FenixEdu Team O nly
a feature? Create Jira Issue Missing Something? Jira Issue Issued FenixEdu Team O nly RT Ticket Assigned Close Issues Is From RT? Return RT Ticket to Issuer Request More Work Yup Yup Task given by Senior Staff Nop Nop
are here to help • You will get stuck in some parts. Not everything is easy to understand or documented. • ONLY bother us after you tried something. This will increase your knowledge of the system. “I don’t know” or “I haven’t tried” is not a good answer. FenixEdu Team O nly
https://confluence.fenixedu.org/display/ACADEMIC/Getting+Started • New functionalities, particularly new modules MUST have comprehensive tests and documentation (JavaDoc, external documentation). FenixEdu Team O nly
being integrated. • You are on a public open source project. Your code will be read by other people, including future employers. Don’t write crap you aren’t comfortable showing off. FenixEdu Team O nly
normally use Git on the console. • You can use whatever you want. • If you aren’t experienced with IDEs, large teams, or working on a professional environment, use what we use. If a solution exists for a problem, everyone benefits FenixEdu Team O nly